mirror of
https://github.com/mcloh/poc-grok-websearch.git
synced 2026-11-06 06:14:11 +00:00
51 lines
3.3 KiB
Python
51 lines
3.3 KiB
Python
import os,time,secrets,asyncio
|
|
from datetime import datetime
|
|
from zoneinfo import ZoneInfo
|
|
from urllib.parse import urlparse
|
|
import requests
|
|
from fastapi import FastAPI,HTTPException,Depends
|
|
from fastapi.security import HTTPBasic,HTTPBasicCredentials
|
|
from fastapi.responses import FileResponse
|
|
from pydantic import BaseModel,Field
|
|
|
|
app=FastAPI(title='Grok OCI · Web Search',docs_url=None,redoc_url=None)
|
|
auth=HTTPBasic()
|
|
def authorize(c:HTTPBasicCredentials=Depends(auth)):
|
|
if not (secrets.compare_digest(c.username,'poc') and secrets.compare_digest(c.password,os.environ['APP_PASSWORD'])):
|
|
raise HTTPException(401,'Acesso restrito',headers={'WWW-Authenticate':'Basic'})
|
|
class Turn(BaseModel):
|
|
role:str
|
|
content:str=Field(max_length=8000)
|
|
class Chat(BaseModel):
|
|
message:str=Field(min_length=1,max_length=8000)
|
|
history:list[Turn]=Field(default_factory=list,max_length=12)
|
|
@app.get('/health')
|
|
def health():return {'status':'ok','provider':'OCI','model':os.environ.get('OCI_MODEL','xai.grok-4.3'),'tool':'web_search'}
|
|
@app.get('/',dependencies=[Depends(authorize)])
|
|
def index():return FileResponse('static/index.html')
|
|
@app.get('/app.js',dependencies=[Depends(authorize)])
|
|
def js():return FileResponse('static/app.js',media_type='application/javascript')
|
|
lock=asyncio.Semaphore(2)
|
|
@app.post('/api/chat',dependencies=[Depends(authorize)])
|
|
async def chat(q:Chat):
|
|
if any(t.role not in ('user','assistant') for t in q.history):raise HTTPException(422,'Papel inválido')
|
|
base=os.environ['OCI_BASE_URL'].rstrip('/')
|
|
if urlparse(base).hostname!='inference.generativeai.us-chicago-1.oci.oraclecloud.com':raise HTTPException(503,'Endpoint OCI inválido')
|
|
now=datetime.now(ZoneInfo('America/Sao_Paulo'))
|
|
body={'model':os.environ.get('OCI_MODEL','xai.grok-4.3'),'input':[t.model_dump() for t in q.history]+[{'role':'user','content':q.message}],
|
|
'instructions':f'Você é um assistente em português brasileiro. Hoje é {now.isoformat()}. Use a ferramenta web_search para verificar informações atuais. Cite fontes com URLs e datas. Diferencie data de publicação e data do fato. Nunca invente notícias; se não houver evidência de hoje, diga isso claramente.',
|
|
'tools':[{'type':'web_search'}],'store':False,'max_output_tokens':2400}
|
|
started=time.monotonic()
|
|
async with lock:
|
|
try:r=await asyncio.to_thread(requests.post,base+'/responses',json=body,headers={'Authorization':'Bearer '+os.environ['OCI_API_KEY'],'OpenAI-Project':os.environ['OCI_PROJECT_ID']},timeout=(10,150))
|
|
except requests.RequestException:raise HTTPException(502,'Falha de conexão com OCI')
|
|
if r.status_code!=200:raise HTTPException(502,{'message':'OCI recusou a chamada','provider_status':r.status_code})
|
|
d=r.json();texts=[];citations=[];calls=[]
|
|
for item in d.get('output',[]):
|
|
if item.get('type')=='web_search_call':calls.append(item)
|
|
for c in item.get('content',[]):
|
|
if c.get('text'):texts.append(c['text'])
|
|
for a in c.get('annotations',[]):
|
|
if a.get('type')=='url_citation':citations.append(a)
|
|
return {'answer':'\n'.join(texts),'citations':citations,'web_search_calls':calls,'usage':d.get('usage'),'response_id':d.get('id'),'model':d.get('model'),'provider':'OCI','timestamp':now.isoformat(),'seconds':round(time.monotonic()-started,2),'raw_response':d}
|