first commit
This commit is contained in:
@@ -7,7 +7,7 @@ AGENT_URL = os.getenv("AGENT_URL", "http://ai-agent:8080")
|
|||||||
OPENAI_MODEL = os.getenv("OPENAI_MODEL", "gpt-4o-mini")
|
OPENAI_MODEL = os.getenv("OPENAI_MODEL", "gpt-4o-mini")
|
||||||
|
|
||||||
def _read_api_key():
|
def _read_api_key():
|
||||||
# Prefer file from Docker secret if present
|
# Prefer Docker secret if available
|
||||||
path = os.getenv("OPENAI_API_KEY_FILE", "/run/secrets/openai_api_key")
|
path = os.getenv("OPENAI_API_KEY_FILE", "/run/secrets/openai_api_key")
|
||||||
if os.path.exists(path):
|
if os.path.exists(path):
|
||||||
return open(path, "r").read().strip()
|
return open(path, "r").read().strip()
|
||||||
@@ -18,7 +18,7 @@ SYSTEM_PROMPT = (
|
|||||||
"with fields: action (scale|restart_service), params (dict). No prose. Examples: "
|
"with fields: action (scale|restart_service), params (dict). No prose. Examples: "
|
||||||
'{"action":"scale","params":{"service":"weblabs_php","replicas":3}} '
|
'{"action":"scale","params":{"service":"weblabs_php","replicas":3}} '
|
||||||
'or {"action":"restart_service","params":{"service":"weblabs_php"}}. '
|
'or {"action":"restart_service","params":{"service":"weblabs_php"}}. '
|
||||||
"Only produce valid JSON. If unclear, choose the safest no-op."
|
"Only produce valid JSON."
|
||||||
)
|
)
|
||||||
|
|
||||||
class ChatIn(BaseModel):
|
class ChatIn(BaseModel):
|
||||||
@@ -36,14 +36,16 @@ async def chat(inp: ChatIn):
|
|||||||
if not api_key:
|
if not api_key:
|
||||||
raise HTTPException(500, "Missing OPENAI_API_KEY (env or secret).")
|
raise HTTPException(500, "Missing OPENAI_API_KEY (env or secret).")
|
||||||
|
|
||||||
# Call OpenAI Responses API
|
url = "https://api.openai.com/v1/chat/completions"
|
||||||
url = "https://api.openai.com/v1/responses"
|
|
||||||
headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
|
headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
|
||||||
body = {
|
body = {
|
||||||
"model": OPENAI_MODEL,
|
"model": OPENAI_MODEL,
|
||||||
"input": f"{SYSTEM_PROMPT}\nUSER: {inp.prompt}",
|
"response_format": {"type": "json_object"},
|
||||||
"max_output_tokens": 300,
|
"temperature": 0.1,
|
||||||
"temperature": 0.1
|
"messages": [
|
||||||
|
{"role": "system", "content": SYSTEM_PROMPT},
|
||||||
|
{"role": "user", "content": inp.prompt},
|
||||||
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
async with httpx.AsyncClient(timeout=30) as client:
|
async with httpx.AsyncClient(timeout=30) as client:
|
||||||
@@ -51,22 +53,12 @@ async def chat(inp: ChatIn):
|
|||||||
if r.status_code >= 400:
|
if r.status_code >= 400:
|
||||||
raise HTTPException(502, f"OpenAI error: {r.text}")
|
raise HTTPException(502, f"OpenAI error: {r.text}")
|
||||||
data = r.json()
|
data = r.json()
|
||||||
# Responses API returns output in 'output_text' (or tool messages). Try common fields.
|
|
||||||
content = data.get("output_text") or data.get("content") or ""
|
|
||||||
if isinstance(content, list):
|
|
||||||
# Some responses return a list of content parts; take text from first text part
|
|
||||||
for part in content:
|
|
||||||
if part.get("type") in ("output_text", "text") and part.get("text"):
|
|
||||||
content = part["text"]
|
|
||||||
break
|
|
||||||
if not isinstance(content, str):
|
|
||||||
content = str(content)
|
|
||||||
|
|
||||||
# Parse JSON from the model output
|
|
||||||
try:
|
try:
|
||||||
|
content = data["choices"][0]["message"]["content"]
|
||||||
cmd = json.loads(content)
|
cmd = json.loads(content)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(500, f"Failed to parse model JSON: {e}; content={content[:200]}")
|
raise HTTPException(500, f"Failed to parse model JSON: {e}; raw={str(data)[:300]}")
|
||||||
|
|
||||||
# Forward to the agent
|
# Forward to the agent
|
||||||
async with httpx.AsyncClient(timeout=15) as client:
|
async with httpx.AsyncClient(timeout=15) as client:
|
||||||
|
|||||||
Reference in New Issue
Block a user