Skip to content

Commit 89e770e

Browse files
committed
.
1 parent a370cc2 commit 89e770e

File tree

1 file changed

+58
-9
lines changed

1 file changed

+58
-9
lines changed

backend/onyx/chat/answer_scratchpad.py

Lines changed: 58 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,14 @@
1919
from agents import Agent
2020
from agents import AgentHooks
2121
from agents import function_tool
22+
from agents import handoff
2223
from agents import ModelSettings
2324
from agents import RunContextWrapper
2425
from agents import Runner
2526
from agents.extensions.handoff_prompt import prompt_with_handoff_instructions
27+
from agents.extensions.handoff_prompt import RECOMMENDED_PROMPT_PREFIX
2628
from agents.extensions.models.litellm_model import LitellmModel
29+
from agents.handoffs import HandoffInputData
2730
from agents.stream_events import RawResponsesStreamEvent
2831
from agents.stream_events import RunItemStreamEvent
2932
from braintrust import traced
@@ -58,6 +61,7 @@
5861
@dataclass
5962
class RunDependencies:
6063
emitter: Emitter
64+
llm: LLM
6165
search_tool: SearchTool | None = None
6266

6367

@@ -66,6 +70,7 @@ class MyContext:
6670
"""Context class to hold search tool and other dependencies"""
6771

6872
run_dependencies: RunDependencies | None = None
73+
needs_compaction: bool = False
6974

7075

7176
def short_tag(link: str, i: int) -> str:
@@ -128,7 +133,7 @@ def llm_completion(
128133
model=model_name,
129134
temperature=temperature,
130135
messages=messages,
131-
tools=None,
136+
tools=[],
132137
stream=stream,
133138
)
134139

@@ -243,6 +248,7 @@ async def amain():
243248
run_dependencies=RunDependencies(
244249
search_tool=search_tool,
245250
emitter=emitter,
251+
llm=llm,
246252
)
247253
)
248254
# 1) start the streamed run (async)
@@ -291,17 +297,39 @@ def finalize_report():
291297
}
292298

293299

294-
class VerboseHooks(AgentHooks[Any]):
300+
class CompactionHooks(AgentHooks[Any]):
295301
async def on_llm_start(
296302
self,
297-
context: RunContextWrapper[Any],
303+
context: RunContextWrapper[MyContext],
298304
agent: Agent[Any],
299305
system_prompt: Optional[str],
300-
input_items: List[dict], # alias: TResponseInputItem
306+
input_items: List[dict],
301307
) -> None:
302308
print(f"[{agent.name}] LLM start")
303309
print("system_prompt:", system_prompt)
304310
print("usage so far:", context.usage.total_tokens)
311+
usage = context.usage.total_tokens
312+
if usage > 10000:
313+
context.context.needs_compaction = True
314+
315+
316+
def compaction_input_filter(input_data: HandoffInputData):
317+
filtered_messages = []
318+
for msg in input_data.input_history[:-1]:
319+
if isinstance(msg, dict) and msg.get("content") is not None:
320+
# Convert tool messages to user messages to avoid API errors
321+
if msg.get("role") == "tool":
322+
filtered_msg = {
323+
"role": "user",
324+
"content": f"Tool response: {msg.get('content', '')}",
325+
}
326+
filtered_messages.append(filtered_msg)
327+
else:
328+
filtered_messages.append(msg)
329+
330+
# Only proceed with compaction if we have valid messages
331+
if filtered_messages:
332+
return [filtered_messages[-1]]
305333

306334

307335
def construct_deep_research_agent(llm: LLM) -> Agent:
@@ -313,7 +341,8 @@ def construct_deep_research_agent(llm: LLM) -> Agent:
313341
api_key=llm.config.api_key,
314342
)
315343

316-
DR_INSTRUCTIONS = """
344+
DR_INSTRUCTIONS = f"""
345+
{RECOMMENDED_PROMPT_PREFIX}
317346
You are a deep-research agent. Work in explicit iterations:
318347
1) PLAN: Decompose the user’s query into sub-questions and a step-by-step plan.
319348
2) SEARCH: Use web_search to explore multiple angles, fanning out and searching in parallel.
@@ -343,7 +372,7 @@ def construct_deep_research_agent(llm: LLM) -> Agent:
343372
# optional: let model choose tools freely
344373
# tool_choice="auto", # if supported by your LitellmModel wrapper
345374
),
346-
hooks=VerboseHooks(),
375+
hooks=CompactionHooks(),
347376
)
348377

349378

@@ -558,15 +587,35 @@ def dr_turn(
558587
)
559588
turn_event_stream_emitter.emit(kind="done", data={"ok": True})
560589
return
561-
562-
agent = construct_deep_research_agent(llm)
590+
dr_agent = construct_deep_research_agent(llm)
591+
compactor_agent = Agent(
592+
name="Compactor",
593+
instructions=f"""
594+
{RECOMMENDED_PROMPT_PREFIX}
595+
Summarize the full conversation so far into JSON with keys:\n
596+
- summary: concise timeline of what happened so far\n
597+
- facts: bullet list of stable facts (IDs, URLs, constraints)\n
598+
- open_questions: bullet list of TODOs / follow-ups\n
599+
Set already_compacted=true to prevent immediate re-compaction.
600+
Then hand off to deep research agent.
601+
""",
602+
output_type=dict,
603+
handoffs=[
604+
handoff(
605+
agent=dr_agent,
606+
input_filter=compaction_input_filter,
607+
)
608+
],
609+
tool_use_behavior="stop_on_first_tool",
610+
)
563611
ctx = MyContext(
564612
run_dependencies=RunDependencies(
565613
search_tool=search_tool,
566614
emitter=turn_event_stream_emitter,
615+
llm=llm,
567616
)
568617
)
569-
bridge = StreamBridge(agent, messages, ctx, max_turns=100).start()
618+
bridge = StreamBridge(compactor_agent, messages, ctx, max_turns=100).start()
570619
for ev in bridge.events():
571620
if isinstance(ev, RunItemStreamEvent):
572621
pass

0 commit comments

Comments
 (0)