|
5 | 5 | import uvicorn |
6 | 6 | from fastapi import FastAPI, HTTPException |
7 | 7 | from pydantic import BaseModel |
8 | | -import requests |
| 8 | +from langfuse.openai import openai # pyright: ignore[reportPrivateImportUsage] |
9 | 9 |
|
10 | 10 |
|
11 | 11 | app = FastAPI() |
@@ -42,52 +42,50 @@ def init(req: InitRequest): |
42 | 42 | # Persist state |
43 | 43 | _STATE[req.rollout_id] = {"terminated": False} |
44 | 44 |
|
45 | | - # Kick off worker thread that runs multi-turn chat via LiteLLM proxy |
| 45 | + # Kick off worker thread that runs multi-turn chat via Langfuse OpenAI integration |
46 | 46 | def _worker(): |
47 | 47 | try: |
48 | | - base_url = os.getenv( |
49 | | - "LITELLM_BASE_URL", |
50 | | - "https://litellm-cloud-proxy-prod-644257448872.us-central1.run.app", |
51 | | - ) |
52 | | - url = f"{base_url}/v1/chat/completions" |
53 | | - headers = { |
54 | | - "Authorization": f"Bearer {os.getenv('FIREWORKS_API_KEY', '')}", |
55 | | - "Content-Type": "application/json", |
56 | | - } |
57 | | - |
58 | | - # Prepare metadata payload to attach for Langfuse filtering |
| 48 | + # Prepare tags for Langfuse filtering |
59 | 49 | metadata = { |
60 | | - "tags": [ |
| 50 | + "langfuse_tags": [ |
61 | 51 | f"invocation_id:{req.metadata.get('invocation_id')}", |
62 | 52 | f"experiment_id:{req.metadata.get('experiment_id')}", |
63 | 53 | f"rollout_id:{req.metadata.get('rollout_id')}", |
64 | 54 | f"run_id:{req.metadata.get('run_id')}", |
65 | 55 | f"row_id:{req.metadata.get('row_id')}", |
66 | | - ], |
67 | | - "invocation_id": req.metadata.get("invocation_id"), |
68 | | - "experiment_id": req.metadata.get("experiment_id"), |
69 | | - "rollout_id": req.metadata.get("rollout_id"), |
70 | | - "run_id": req.metadata.get("run_id"), |
71 | | - "row_id": req.metadata.get("row_id"), |
| 56 | + ] |
72 | 57 | } |
73 | 58 |
|
74 | 59 | messages = req.messages |
75 | 60 |
|
76 | 61 | # Simulate N-1 assistant turns (single-shot or simple echo) |
77 | 62 | for _ in range(max(1, req.num_turns)): |
78 | | - payload = { |
| 63 | + completion_kwargs = { |
79 | 64 | "model": req.model, |
80 | 65 | "messages": _clean_messages_for_api(messages), |
81 | 66 | "metadata": metadata, |
82 | 67 | } |
| 68 | + |
83 | 69 | if req.tools: |
84 | | - payload["tools"] = req.tools |
85 | | - r = requests.post(url, json=payload, headers=headers, timeout=60) |
86 | | - r.raise_for_status() |
87 | | - data = r.json() |
88 | | - assistant = data.get("choices", [{}])[0].get("message", {}) |
| 70 | + completion_kwargs["tools"] = req.tools |
| 71 | + |
| 72 | + completion = openai.chat.completions.create(**completion_kwargs) |
| 73 | + assistant_message = completion.choices[0].message |
| 74 | + |
| 75 | + # Convert to dict format for next turn |
| 76 | + assistant_dict = {"role": "assistant", "content": assistant_message.content} |
| 77 | + if assistant_message.tool_calls: |
| 78 | + assistant_dict["tool_calls"] = [ |
| 79 | + { |
| 80 | + "id": tc.id, |
| 81 | + "type": tc.type, |
| 82 | + "function": {"name": tc.function.name, "arguments": tc.function.arguments}, |
| 83 | + } |
| 84 | + for tc in assistant_message.tool_calls |
| 85 | + ] |
| 86 | + |
89 | 87 | # Append assistant for next turn |
90 | | - messages = messages + [assistant] |
| 88 | + messages = messages + [assistant_dict] |
91 | 89 |
|
92 | 90 | except Exception: |
93 | 91 | # Best-effort; mark as done even on error to unblock polling |
|
0 commit comments