Skip to content

Commit 8b861d2

Browse files
fix source provisioning
1 parent 1f50488 commit 8b861d2

File tree

2 files changed

+18
-15
lines changed

2 files changed

+18
-15
lines changed

core/agent.py

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ def __init__(
3535
model: str = None,
3636
):
3737
self.document_service = document_service
38-
self.sources = {}
3938
# Load settings
4039
self.settings = get_settings()
4140
self.model = model or self.settings.AGENT_MODEL
@@ -100,20 +99,22 @@ def __init__(
10099
when citing different sources. Use markdown formatting for text content to improve readability.
101100
""".strip()
102101

103-
async def _execute_tool(self, name: str, args: dict, auth: AuthContext):
102+
async def _execute_tool(self, name: str, args: dict, auth: AuthContext, source_map: dict):
104103
"""Dispatch tool calls, injecting document_service and auth."""
105104
match name:
106105
case "retrieve_chunks":
107-
content, sources = await retrieve_chunks(document_service=self.document_service, auth=auth, **args)
108-
self.sources.update(sources)
106+
content, found_sources = await retrieve_chunks(
107+
document_service=self.document_service, auth=auth, **args
108+
)
109+
source_map.update(found_sources)
109110
return content
110111
case "retrieve_document":
111112
result = await retrieve_document(document_service=self.document_service, auth=auth, **args)
112113
# Add document as a source if it's a successful retrieval
113114
if isinstance(result, str) and not result.startswith("Document") and not result.startswith("Error"):
114115
doc_id = args.get("document_id", "unknown")
115116
source_id = f"doc{doc_id}-full"
116-
self.sources[source_id] = {
117+
source_map[source_id] = {
117118
"document_id": doc_id,
118119
"document_name": f"Full Document {doc_id}",
119120
"chunk_number": "full",
@@ -126,7 +127,7 @@ async def _execute_tool(self, name: str, args: dict, auth: AuthContext):
126127
doc_id = args.get("document_id")
127128
analysis_type = args.get("analysis_type", "analysis")
128129
source_id = f"doc{doc_id}-{analysis_type}"
129-
self.sources[source_id] = {
130+
source_map[source_id] = {
130131
"document_id": doc_id,
131132
"document_name": f"Document {doc_id} ({analysis_type})",
132133
"analysis_type": analysis_type,
@@ -148,6 +149,8 @@ async def _execute_tool(self, name: str, args: dict, auth: AuthContext):
148149

149150
async def run(self, query: str, auth: AuthContext) -> str:
150151
"""Synchronously run the agent and return the final answer."""
152+
# Per-run state to avoid cross-request leakage
153+
source_map: dict = {}
151154
messages = [
152155
{"role": "system", "content": self.system_prompt},
153156
{"role": "user", "content": query},
@@ -212,8 +215,8 @@ async def run(self, query: str, auth: AuthContext) -> str:
212215
}
213216
if "caption" in item and item["type"] == "image":
214217
display_obj["caption"] = item["caption"]
215-
if item["type"] == "image":
216-
display_obj["content"] = self.sources[item["source"]]["content"]
218+
if item["type"] == "image" and item.get("source") in source_map:
219+
display_obj["content"] = source_map[item["source"]]["content"]
217220
display_objects.append(display_obj)
218221
elif (
219222
isinstance(parsed_content, dict)
@@ -228,8 +231,8 @@ async def run(self, query: str, auth: AuthContext) -> str:
228231
}
229232
if "caption" in parsed_content and parsed_content["type"] == "image":
230233
display_obj["caption"] = parsed_content["caption"]
231-
if item["type"] == "image":
232-
display_obj["content"] = self.sources[item["source"]]["content"]
234+
if parsed_content.get("type") == "image" and parsed_content.get("source") in source_map:
235+
display_obj["content"] = source_map[parsed_content["source"]]["content"]
233236
display_objects.append(display_obj)
234237

235238
# If no display objects were created, treat the entire content as text
@@ -260,7 +263,7 @@ async def run(self, query: str, auth: AuthContext) -> str:
260263
"sourceId": source_id,
261264
"documentName": f"Document {doc_id}",
262265
"documentId": doc_id,
263-
"content": self.sources.get(source_id, {"content": ""})["content"],
266+
"content": source_map.get(source_id, {"content": ""}).get("content", ""),
264267
}
265268
)
266269
else:
@@ -269,7 +272,7 @@ async def run(self, query: str, auth: AuthContext) -> str:
269272
"sourceId": source_id,
270273
"documentName": "Referenced Source",
271274
"documentId": "unknown",
272-
"content": self.sources.get(source_id, {"content": ""})["content"],
275+
"content": source_map.get(source_id, {"content": ""}).get("content", ""),
273276
}
274277
)
275278

@@ -285,7 +288,7 @@ async def run(self, query: str, auth: AuthContext) -> str:
285288
)
286289

287290
# Add sources from document chunks used during the session
288-
for source_id, source_info in self.sources.items():
291+
for source_id, source_info in source_map.items():
289292
if source_id not in seen_source_ids:
290293
sources.append(
291294
{
@@ -314,7 +317,7 @@ async def run(self, query: str, auth: AuthContext) -> str:
314317
# messages.append({'role': 'assistant', 'content': msg.content})
315318
messages.append(msg.to_dict(exclude_none=True))
316319
logger.info(f"Executing tool: {name}")
317-
result = await self._execute_tool(name, args, auth)
320+
result = await self._execute_tool(name, args, auth, source_map)
318321
logger.info(f"Tool execution result: {result}")
319322

320323
# Add tool call and result to history

core/api.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -965,7 +965,7 @@ async def agent_query(request: AgentQueryRequest, auth: AuthContext = Depends(ve
965965
# Check free-tier agent call limits in cloud mode
966966
if settings.MODE == "cloud" and auth.user_id:
967967
await check_and_increment_limits(auth, "agent", 1)
968-
# Use shared agent instance and pass auth to run
968+
# Use the shared MorphikAgent instance; per-run state is now isolated internally
969969
response = await morphik_agent.run(request.query, auth)
970970
# Return the complete response dictionary
971971
return response

0 commit comments

Comments
 (0)