rossum-agent 1.0.0rc3__py3-none-any.whl → 1.0.0rc5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rossum_agent/__init__.py +1 -1
- rossum_agent/agent/core.py +0 -31
- rossum_agent/api/models/schemas.py +4 -0
- rossum_agent/api/routes/messages.py +5 -0
- rossum_agent/api/services/agent_service.py +53 -25
- rossum_agent/prompts/base_prompt.py +1 -1
- {rossum_agent-1.0.0rc3.dist-info → rossum_agent-1.0.0rc5.dist-info}/METADATA +6 -1
- {rossum_agent-1.0.0rc3.dist-info → rossum_agent-1.0.0rc5.dist-info}/RECORD +12 -13
- rossum_agent/agent/request_classifier.py +0 -152
- {rossum_agent-1.0.0rc3.dist-info → rossum_agent-1.0.0rc5.dist-info}/WHEEL +0 -0
- {rossum_agent-1.0.0rc3.dist-info → rossum_agent-1.0.0rc5.dist-info}/entry_points.txt +0 -0
- {rossum_agent-1.0.0rc3.dist-info → rossum_agent-1.0.0rc5.dist-info}/licenses/LICENSE +0 -0
- {rossum_agent-1.0.0rc3.dist-info → rossum_agent-1.0.0rc5.dist-info}/top_level.txt +0 -0
rossum_agent/__init__.py
CHANGED
rossum_agent/agent/core.py
CHANGED
|
@@ -75,7 +75,6 @@ from rossum_agent.agent.models import (
|
|
|
75
75
|
ToolResult,
|
|
76
76
|
truncate_content,
|
|
77
77
|
)
|
|
78
|
-
from rossum_agent.agent.request_classifier import RequestScope, classify_request, generate_rejection_response
|
|
79
78
|
from rossum_agent.api.models.schemas import TokenUsageBreakdown
|
|
80
79
|
from rossum_agent.bedrock_client import create_bedrock_client, get_model_id
|
|
81
80
|
from rossum_agent.rossum_mcp_integration import MCPConnection, mcp_tools_to_anthropic_format
|
|
@@ -769,32 +768,6 @@ class RossumAgent:
|
|
|
769
768
|
text_parts.append(text)
|
|
770
769
|
return " ".join(text_parts)
|
|
771
770
|
|
|
772
|
-
def _check_request_scope(self, prompt: UserContent) -> AgentStep | None:
|
|
773
|
-
"""Check if request is in scope, return rejection step if out of scope."""
|
|
774
|
-
text = self._extract_text_from_prompt(prompt)
|
|
775
|
-
result = classify_request(self.client, text)
|
|
776
|
-
self._total_input_tokens += result.input_tokens
|
|
777
|
-
self._total_output_tokens += result.output_tokens
|
|
778
|
-
self._main_agent_input_tokens += result.input_tokens
|
|
779
|
-
self._main_agent_output_tokens += result.output_tokens
|
|
780
|
-
if result.scope == RequestScope.OUT_OF_SCOPE:
|
|
781
|
-
rejection = generate_rejection_response(self.client, text)
|
|
782
|
-
total_input = result.input_tokens + rejection.input_tokens
|
|
783
|
-
total_output = result.output_tokens + rejection.output_tokens
|
|
784
|
-
self._total_input_tokens += rejection.input_tokens
|
|
785
|
-
self._total_output_tokens += rejection.output_tokens
|
|
786
|
-
self._main_agent_input_tokens += rejection.input_tokens
|
|
787
|
-
self._main_agent_output_tokens += rejection.output_tokens
|
|
788
|
-
return AgentStep(
|
|
789
|
-
step_number=1,
|
|
790
|
-
final_answer=rejection.response,
|
|
791
|
-
is_final=True,
|
|
792
|
-
input_tokens=total_input,
|
|
793
|
-
output_tokens=total_output,
|
|
794
|
-
step_type=StepType.FINAL_ANSWER,
|
|
795
|
-
)
|
|
796
|
-
return None
|
|
797
|
-
|
|
798
771
|
def _inject_preload_info(self, prompt: UserContent, preload_result: str) -> UserContent:
|
|
799
772
|
"""Inject preload result info into the user prompt."""
|
|
800
773
|
suffix = (
|
|
@@ -820,10 +793,6 @@ class RossumAgent:
|
|
|
820
793
|
|
|
821
794
|
Rate limiting is handled with exponential backoff and jitter.
|
|
822
795
|
"""
|
|
823
|
-
if rejection := self._check_request_scope(prompt):
|
|
824
|
-
yield rejection
|
|
825
|
-
return
|
|
826
|
-
|
|
827
796
|
loop = asyncio.get_event_loop()
|
|
828
797
|
mcp_mode = get_mcp_mode()
|
|
829
798
|
set_mcp_connection(self.mcp_connection, loop, mcp_mode)
|
|
@@ -133,6 +133,10 @@ class MessageRequest(BaseModel):
|
|
|
133
133
|
description="Optional list of PDF documents (max 5) to include with the message",
|
|
134
134
|
)
|
|
135
135
|
rossum_url: str | None = Field(default=None, description="Optional Rossum app URL for context")
|
|
136
|
+
mcp_mode: Literal["read-only", "read-write"] | None = Field(
|
|
137
|
+
default=None,
|
|
138
|
+
description="MCP mode to use for this message and all subsequent messages. If not specified, uses the chat's current mode.",
|
|
139
|
+
)
|
|
136
140
|
|
|
137
141
|
|
|
138
142
|
class StepEvent(BaseModel):
|
|
@@ -157,6 +157,11 @@ async def send_message(
|
|
|
157
157
|
|
|
158
158
|
history = chat_data.messages
|
|
159
159
|
mcp_mode = chat_data.metadata.mcp_mode
|
|
160
|
+
# Use message-level mode if provided, otherwise use chat's mode
|
|
161
|
+
if message.mcp_mode is not None:
|
|
162
|
+
mcp_mode = message.mcp_mode
|
|
163
|
+
# Update chat metadata with new mode for future messages
|
|
164
|
+
chat_data.metadata.mcp_mode = mcp_mode
|
|
160
165
|
user_prompt = message.content
|
|
161
166
|
images: list[ImageContent] | None = message.images
|
|
162
167
|
documents: list[DocumentContent] | None = message.documents
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
|
+
import contextvars
|
|
6
7
|
import logging
|
|
8
|
+
from dataclasses import dataclass
|
|
7
9
|
from typing import TYPE_CHECKING, Any, Literal
|
|
8
10
|
|
|
9
11
|
from rossum_agent.agent.core import RossumAgent, create_agent
|
|
@@ -43,6 +45,18 @@ if TYPE_CHECKING:
|
|
|
43
45
|
logger = logging.getLogger(__name__)
|
|
44
46
|
|
|
45
47
|
|
|
48
|
+
@dataclass
|
|
49
|
+
class _RequestContext:
|
|
50
|
+
"""Per-request context for agent execution."""
|
|
51
|
+
|
|
52
|
+
output_dir: Path | None = None
|
|
53
|
+
sub_agent_queue: asyncio.Queue[SubAgentProgressEvent | SubAgentTextEvent] | None = None
|
|
54
|
+
last_memory: AgentMemory | None = None
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
_request_context: contextvars.ContextVar[_RequestContext] = contextvars.ContextVar("request_context")
|
|
58
|
+
|
|
59
|
+
|
|
46
60
|
def convert_sub_agent_progress_to_event(progress: SubAgentProgress) -> SubAgentProgressEvent:
|
|
47
61
|
"""Convert a SubAgentProgress to a SubAgentProgressEvent for SSE streaming.
|
|
48
62
|
|
|
@@ -133,28 +147,36 @@ class AgentService:
|
|
|
133
147
|
"""Service for running the Rossum Agent.
|
|
134
148
|
|
|
135
149
|
Manages MCP connection lifecycle and agent execution for API requests.
|
|
150
|
+
Uses contextvars for per-request state to support concurrent requests.
|
|
136
151
|
"""
|
|
137
152
|
|
|
138
153
|
def __init__(self) -> None:
|
|
139
154
|
"""Initialize agent service."""
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
155
|
+
|
|
156
|
+
def _get_context(self) -> _RequestContext:
|
|
157
|
+
"""Get the current request context, creating if needed."""
|
|
158
|
+
try:
|
|
159
|
+
return _request_context.get()
|
|
160
|
+
except LookupError:
|
|
161
|
+
ctx = _RequestContext()
|
|
162
|
+
_request_context.set(ctx)
|
|
163
|
+
return ctx
|
|
143
164
|
|
|
144
165
|
@property
|
|
145
166
|
def output_dir(self) -> Path | None:
|
|
146
167
|
"""Get the output directory for the current run."""
|
|
147
|
-
return self.
|
|
168
|
+
return self._get_context().output_dir
|
|
148
169
|
|
|
149
170
|
def _on_sub_agent_progress(self, progress: SubAgentProgress) -> None:
|
|
150
171
|
"""Callback for sub-agent progress updates.
|
|
151
172
|
|
|
152
173
|
Converts the progress to an event and puts it on the queue for streaming.
|
|
153
174
|
"""
|
|
154
|
-
|
|
175
|
+
ctx = self._get_context()
|
|
176
|
+
if ctx.sub_agent_queue is not None:
|
|
155
177
|
event = convert_sub_agent_progress_to_event(progress)
|
|
156
178
|
try:
|
|
157
|
-
|
|
179
|
+
ctx.sub_agent_queue.put_nowait(event)
|
|
158
180
|
except asyncio.QueueFull:
|
|
159
181
|
logger.warning("Sub-agent progress queue full, dropping event")
|
|
160
182
|
|
|
@@ -163,10 +185,11 @@ class AgentService:
|
|
|
163
185
|
|
|
164
186
|
Converts the text to an event and puts it on the queue for streaming.
|
|
165
187
|
"""
|
|
166
|
-
|
|
188
|
+
ctx = self._get_context()
|
|
189
|
+
if ctx.sub_agent_queue is not None:
|
|
167
190
|
event = SubAgentTextEvent(tool_name=text.tool_name, text=text.text, is_final=text.is_final)
|
|
168
191
|
try:
|
|
169
|
-
|
|
192
|
+
ctx.sub_agent_queue.put_nowait(event)
|
|
170
193
|
except asyncio.QueueFull:
|
|
171
194
|
logger.warning("Sub-agent text queue full, dropping event")
|
|
172
195
|
|
|
@@ -196,16 +219,19 @@ class AgentService:
|
|
|
196
219
|
if documents:
|
|
197
220
|
logger.info(f"Including {len(documents)} documents in the prompt")
|
|
198
221
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
222
|
+
ctx = _RequestContext()
|
|
223
|
+
_request_context.set(ctx)
|
|
224
|
+
|
|
225
|
+
ctx.output_dir = create_session_output_dir()
|
|
226
|
+
set_session_output_dir(ctx.output_dir)
|
|
227
|
+
set_output_dir(ctx.output_dir)
|
|
202
228
|
set_rossum_credentials(rossum_api_base_url, rossum_api_token)
|
|
203
|
-
logger.info(f"Created session output directory: {
|
|
229
|
+
logger.info(f"Created session output directory: {ctx.output_dir}")
|
|
204
230
|
|
|
205
231
|
if documents:
|
|
206
232
|
self._save_documents_to_output_dir(documents)
|
|
207
233
|
|
|
208
|
-
|
|
234
|
+
ctx.sub_agent_queue = asyncio.Queue(maxsize=100)
|
|
209
235
|
set_progress_callback(self._on_sub_agent_progress)
|
|
210
236
|
set_text_callback(self._on_sub_agent_text)
|
|
211
237
|
|
|
@@ -237,9 +263,9 @@ class AgentService:
|
|
|
237
263
|
|
|
238
264
|
try:
|
|
239
265
|
async for step in agent.run(user_content):
|
|
240
|
-
while not
|
|
266
|
+
while not ctx.sub_agent_queue.empty():
|
|
241
267
|
try:
|
|
242
|
-
sub_event =
|
|
268
|
+
sub_event = ctx.sub_agent_queue.get_nowait()
|
|
243
269
|
yield sub_event
|
|
244
270
|
except asyncio.QueueEmpty:
|
|
245
271
|
break
|
|
@@ -251,14 +277,14 @@ class AgentService:
|
|
|
251
277
|
total_input_tokens = agent._total_input_tokens
|
|
252
278
|
total_output_tokens = agent._total_output_tokens
|
|
253
279
|
|
|
254
|
-
while not
|
|
280
|
+
while not ctx.sub_agent_queue.empty():
|
|
255
281
|
try:
|
|
256
|
-
sub_event =
|
|
282
|
+
sub_event = ctx.sub_agent_queue.get_nowait()
|
|
257
283
|
yield sub_event
|
|
258
284
|
except asyncio.QueueEmpty:
|
|
259
285
|
break
|
|
260
286
|
|
|
261
|
-
|
|
287
|
+
ctx.last_memory = agent.memory
|
|
262
288
|
|
|
263
289
|
yield StreamDoneEvent(
|
|
264
290
|
total_steps=total_steps,
|
|
@@ -281,7 +307,6 @@ class AgentService:
|
|
|
281
307
|
set_text_callback(None)
|
|
282
308
|
set_output_dir(None)
|
|
283
309
|
set_rossum_credentials(None, None)
|
|
284
|
-
self._sub_agent_queue = None
|
|
285
310
|
|
|
286
311
|
def _save_documents_to_output_dir(self, documents: list[DocumentContent]) -> None:
|
|
287
312
|
"""Save uploaded documents to the output directory.
|
|
@@ -291,12 +316,13 @@ class AgentService:
|
|
|
291
316
|
"""
|
|
292
317
|
import base64 # noqa: PLC0415 - import here to avoid circular import at module level
|
|
293
318
|
|
|
294
|
-
|
|
319
|
+
ctx = self._get_context()
|
|
320
|
+
if ctx.output_dir is None:
|
|
295
321
|
logger.warning("Cannot save documents: output directory not set")
|
|
296
322
|
return
|
|
297
323
|
|
|
298
324
|
for doc in documents:
|
|
299
|
-
file_path =
|
|
325
|
+
file_path = ctx.output_dir / doc.filename
|
|
300
326
|
try:
|
|
301
327
|
file_data = base64.b64decode(doc.data)
|
|
302
328
|
file_path.write_bytes(file_data)
|
|
@@ -320,6 +346,7 @@ class AgentService:
|
|
|
320
346
|
if not images and not documents:
|
|
321
347
|
return prompt
|
|
322
348
|
|
|
349
|
+
ctx = self._get_context()
|
|
323
350
|
content: list[ImageBlockParam | TextBlockParam] = []
|
|
324
351
|
if images:
|
|
325
352
|
for img in images:
|
|
@@ -333,8 +360,8 @@ class AgentService:
|
|
|
333
360
|
},
|
|
334
361
|
}
|
|
335
362
|
)
|
|
336
|
-
if documents and
|
|
337
|
-
doc_paths = [str(
|
|
363
|
+
if documents and ctx.output_dir:
|
|
364
|
+
doc_paths = [str(ctx.output_dir / doc.filename) for doc in documents]
|
|
338
365
|
doc_info = "\n".join(f"- {path}" for path in doc_paths)
|
|
339
366
|
content.append({"type": "text", "text": f"[Uploaded documents available for processing:\n{doc_info}]"})
|
|
340
367
|
content.append({"type": "text", "text": prompt})
|
|
@@ -416,9 +443,10 @@ class AgentService:
|
|
|
416
443
|
images: Optional list of images included with the user prompt.
|
|
417
444
|
documents: Optional list of documents included with the user prompt.
|
|
418
445
|
"""
|
|
419
|
-
|
|
446
|
+
ctx = self._get_context()
|
|
447
|
+
if ctx.last_memory is not None:
|
|
420
448
|
lean_history: list[dict[str, Any]] = []
|
|
421
|
-
for step_dict in
|
|
449
|
+
for step_dict in ctx.last_memory.to_dict():
|
|
422
450
|
if step_dict.get("type") == "task_step":
|
|
423
451
|
lean_history.append(step_dict)
|
|
424
452
|
elif step_dict.get("type") == "memory_step":
|
|
@@ -5,7 +5,7 @@ Optimized for Opus 4.5: Goals + constraints, not procedures.
|
|
|
5
5
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
|
-
ROSSUM_EXPERT_INTRO = """You are an expert Rossum platform specialist. Help users understand, document, debug, and configure document processing workflows.
|
|
8
|
+
ROSSUM_EXPERT_INTRO = """You are an expert Rossum platform specialist. Help users understand, document, debug, and configure document processing workflows. Politely redirect requests unrelated to Rossum.
|
|
9
9
|
|
|
10
10
|
**CRITICAL - Use `search_knowledge_base` before**:
|
|
11
11
|
- Explaining ANY extension/hook behavior (except simple function hooks you can read directly)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rossum-agent
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.0rc5
|
|
4
4
|
Summary: AI agent toolkit for Rossum: document workflows conversationally, debug pipelines automatically, and enable agentic configuration of intelligent document processing.
|
|
5
5
|
Author-email: "Dan Stancl (Rossum AI)" <daniel.stancl@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -161,6 +161,7 @@ uv sync --extra streamlit # Streamlit UI only
|
|
|
161
161
|
| `AWS_DEFAULT_REGION` | No | AWS region (default: `us-east-1`) |
|
|
162
162
|
| `REDIS_HOST` | No | Redis host for chat persistence |
|
|
163
163
|
| `REDIS_PORT` | No | Redis port (default: `6379`) |
|
|
164
|
+
| `ROSSUM_MCP_MODE` | No | MCP mode: `read-only` (default) or `read-write` |
|
|
164
165
|
|
|
165
166
|
## Usage
|
|
166
167
|
|
|
@@ -298,6 +299,10 @@ flowchart TB
|
|
|
298
299
|
|
|
299
300
|
API docs: `/api/docs` (Swagger) or `/api/redoc`
|
|
300
301
|
|
|
302
|
+
**MCP Mode:** Chat sessions support mode switching via the `mcp_mode` parameter:
|
|
303
|
+
- Set at chat creation: `POST /api/v1/chats` with `{"mcp_mode": "read-write"}`
|
|
304
|
+
- Override per message: `POST /api/v1/chats/{id}/messages` with `{"content": "...", "mcp_mode": "read-write"}`
|
|
305
|
+
|
|
301
306
|
</details>
|
|
302
307
|
|
|
303
308
|
## License
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
rossum_agent/__init__.py,sha256
|
|
1
|
+
rossum_agent/__init__.py,sha256=-oMYnv2386qGc8lmYl0hAQ4vVzkgDm6KPqViRdGwz0s,235
|
|
2
2
|
rossum_agent/agent_logging.py,sha256=d6V-VxFEiSgaiMV7JZnUA1Chg34SLU_e3ZF42gKmoZU,1848
|
|
3
3
|
rossum_agent/bedrock_client.py,sha256=Ri3CmeC3DAazT8TnRf0m8rbyd0chSU0p9nI3an04n2E,2423
|
|
4
4
|
rossum_agent/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -8,10 +8,9 @@ rossum_agent/url_context.py,sha256=u8j9_AFSHnZSkmcMVe6lypKKZ5qL148wKDgPmQA-MBc,5
|
|
|
8
8
|
rossum_agent/user_detection.py,sha256=An-B7frKwXg3V0N7tTrbP5YWrsHX2VAk8K7i0JS2JZg,2845
|
|
9
9
|
rossum_agent/utils.py,sha256=UCQEAKBsvGt7OdU1JG30DykPaGwyVb63ciFc-j09F0U,3709
|
|
10
10
|
rossum_agent/agent/__init__.py,sha256=QpdTIYGE5MouIf73gIwXL2hvMTrdeJIMT1aCxwVI04k,753
|
|
11
|
-
rossum_agent/agent/core.py,sha256=
|
|
11
|
+
rossum_agent/agent/core.py,sha256=svZRHXD-4LpEj1UJSiyjNlyIPUXv0ddPY0JhdvqATns,36730
|
|
12
12
|
rossum_agent/agent/memory.py,sha256=lfRyFf1coM0JQ5dMFE3x6SjxnQXJhwj3oa_1Vn5Nj5Y,6194
|
|
13
13
|
rossum_agent/agent/models.py,sha256=fdPZpNfrwxNu3dMz_uiWyyo5j9qnsj8UlppoBZtsz8g,4820
|
|
14
|
-
rossum_agent/agent/request_classifier.py,sha256=ni0Aq5c6fFhDFv8NyBhJeYe5f2lmFoubN8GOZwa5fLs,5616
|
|
15
14
|
rossum_agent/agent/skills.py,sha256=u-YarSh8I4monU0v8n74c2M_04x5oKIEOCukCgdnubQ,3922
|
|
16
15
|
rossum_agent/agent/types.py,sha256=3wlQuHRuVRBeDo22Y-GhVsUwFyJdUdZ8QxmDFsBX6Vg,156
|
|
17
16
|
rossum_agent/api/__init__.py,sha256=t3qzj02YM9fC82ZESBxPnt3z2psrFQ4uZb-iCzomm-M,47
|
|
@@ -19,19 +18,19 @@ rossum_agent/api/cli.py,sha256=_1K2qGQ_WaSKUMbtIQvNV5iiSGf3wBxbSOXM_bPWN8Q,1681
|
|
|
19
18
|
rossum_agent/api/dependencies.py,sha256=i-mDNWiEKuMEEI93o4EIv-0YKByFGw-ALOH7OnkNpg0,6897
|
|
20
19
|
rossum_agent/api/main.py,sha256=LAmUGcbG9hVTZZtz2Mod-b1iMrNPxGmXXYqUiEX4K0U,5898
|
|
21
20
|
rossum_agent/api/models/__init__.py,sha256=j4t5I5ptWWMBA_eS5FL3X6MJiAxTDF53ACnJPAvAZbE,54
|
|
22
|
-
rossum_agent/api/models/schemas.py,sha256=
|
|
21
|
+
rossum_agent/api/models/schemas.py,sha256=7_9VIkx71-QTeTFtp47xVRPEDm4yqtFwH2ZhgEem93M,9311
|
|
23
22
|
rossum_agent/api/routes/__init__.py,sha256=L2rqkYFsGSCU1Qnkgrw5ngCisTmLUPH747uX3ess2NA,26
|
|
24
23
|
rossum_agent/api/routes/chats.py,sha256=0PdFzOWHuxF7ipRqTb0pFSSzT1_MJ-sdn5AgRClOU8s,3845
|
|
25
24
|
rossum_agent/api/routes/files.py,sha256=7SvZ1miOC2fwaFsd5ko9rLCvntgc1YbXK8FchB1aHmk,4539
|
|
26
25
|
rossum_agent/api/routes/health.py,sha256=Sjis2KqfgFkfJAsGFjg5d8BhIswUFhNUfOf56o0MRI4,1435
|
|
27
|
-
rossum_agent/api/routes/messages.py,sha256=
|
|
26
|
+
rossum_agent/api/routes/messages.py,sha256=fHu7Jyf-4T35Jc2U4W9LNr_Ibf_zO_tdeO03AGgPIcA,8417
|
|
28
27
|
rossum_agent/api/services/__init__.py,sha256=oLA7_0cvWSde2ZDAG2lW-KrAj1E2mZDHhgAugftwnNc,40
|
|
29
|
-
rossum_agent/api/services/agent_service.py,sha256=
|
|
28
|
+
rossum_agent/api/services/agent_service.py,sha256=fVzBpPMVhR7iE7X0XlVAnqB7wye03t8sC6lzCt2CPTA,19156
|
|
30
29
|
rossum_agent/api/services/chat_service.py,sha256=sJMRiYmVeIWMVIDFniMC99e03fdIO0IlZnnheh9c180,6745
|
|
31
30
|
rossum_agent/api/services/file_service.py,sha256=9hw5n47ZezRKxD0o9d8dgX4mLDILmP7eCJpsXKtSAOM,1992
|
|
32
31
|
rossum_agent/assets/Primary_light_logo.png,sha256=kKdNl4vB7N2HWjd2WxAe_Hap_PF0LrVHs4rNz066dGA,10441
|
|
33
32
|
rossum_agent/prompts/__init__.py,sha256=_RT2eLtnYobZv4OAQsLFXBRg3pCDxTMGeHGOgLIc97A,714
|
|
34
|
-
rossum_agent/prompts/base_prompt.py,sha256=
|
|
33
|
+
rossum_agent/prompts/base_prompt.py,sha256=bhWmKYWA3NmwjIzwCMHBiXWtLwVzTkhjfB01lGfP1FQ,3555
|
|
35
34
|
rossum_agent/prompts/system_prompt.py,sha256=6ogk00BCIusS-PMfM47F0Fit7OYPANoaWLi0fZal_Y0,688
|
|
36
35
|
rossum_agent/skills/hook-debugging.md,sha256=QKUhvWMyRHJZ70Wpu4VDfPxSmC4wLC6er3NbETJNjiM,1134
|
|
37
36
|
rossum_agent/skills/organization-setup.md,sha256=-BCOGB9Jwpy-ffAkiWAfXI2hN8FaLWKkxTOSIZvGsL8,2307
|
|
@@ -59,9 +58,9 @@ rossum_agent/tools/subagents/hook_debug.py,sha256=5lJiIQAh1nf6uOF05KB4AAjD72cZUw
|
|
|
59
58
|
rossum_agent/tools/subagents/knowledge_base.py,sha256=Xr1I6uscK8ETSwx_bS7ZylgtBI5Cj_p1Pmtd08655BI,10580
|
|
60
59
|
rossum_agent/tools/subagents/mcp_helpers.py,sha256=1yR1gvoWJ1gfzXDcXRwO94viUrRRz7QuRUtCy0dc518,1452
|
|
61
60
|
rossum_agent/tools/subagents/schema_patching.py,sha256=eJWK3HLdZ15EHr7S8myFo8AbX3N8MTN2r1-tAMb4-K4,17377
|
|
62
|
-
rossum_agent-1.0.
|
|
63
|
-
rossum_agent-1.0.
|
|
64
|
-
rossum_agent-1.0.
|
|
65
|
-
rossum_agent-1.0.
|
|
66
|
-
rossum_agent-1.0.
|
|
67
|
-
rossum_agent-1.0.
|
|
61
|
+
rossum_agent-1.0.0rc5.dist-info/licenses/LICENSE,sha256=5nqARgtmPvoIU-1o1az3i8Qi2WOHYIn03vD6haewvEI,1087
|
|
62
|
+
rossum_agent-1.0.0rc5.dist-info/METADATA,sha256=H-br23XJjHLnVuji1N9DE4_t1JgFjjOYEiCzhq3eGdE,11313
|
|
63
|
+
rossum_agent-1.0.0rc5.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
64
|
+
rossum_agent-1.0.0rc5.dist-info/entry_points.txt,sha256=1bbSeeptoKqT5-cWG4BuoMOGh_Crhu1sLL8AHXhbAJ0,115
|
|
65
|
+
rossum_agent-1.0.0rc5.dist-info/top_level.txt,sha256=jQm_Tm7Yq8oB68I8Y1BEhGqMGrusIgqfvfzeO1PGQso,13
|
|
66
|
+
rossum_agent-1.0.0rc5.dist-info/RECORD,,
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
"""Lightweight request classifier for filtering out-of-scope requests.
|
|
2
|
-
|
|
3
|
-
This module provides a fast pre-filter that checks if a user request is within
|
|
4
|
-
the scope of the Rossum platform assistant before engaging the full agent.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
import logging
|
|
10
|
-
from dataclasses import dataclass
|
|
11
|
-
from enum import Enum
|
|
12
|
-
from typing import TYPE_CHECKING
|
|
13
|
-
|
|
14
|
-
from rossum_agent.bedrock_client import get_small_model_id
|
|
15
|
-
|
|
16
|
-
if TYPE_CHECKING:
|
|
17
|
-
from anthropic import AnthropicBedrock
|
|
18
|
-
|
|
19
|
-
logger = logging.getLogger(__name__)
|
|
20
|
-
|
|
21
|
-
CLASSIFIER_PROMPT = """You are a scope classifier for a Rossum document processing platform assistant.
|
|
22
|
-
|
|
23
|
-
The assistant can help with:
|
|
24
|
-
- Queue, hook, schema, and extension analysis/configuration
|
|
25
|
-
- Debugging document processing issues and errors
|
|
26
|
-
- Investigating hook logs and extension behavior
|
|
27
|
-
- Explaining workflows and automation
|
|
28
|
-
- Writing analysis reports about Rossum configuration issues
|
|
29
|
-
|
|
30
|
-
IN_SCOPE: Request relates to Rossum PLATFORM operations
|
|
31
|
-
- setting up new organization
|
|
32
|
-
- analyzing/configuring queues, hooks, schemas, extensions
|
|
33
|
-
- debugging errors, investigating logs
|
|
34
|
-
- explaining workflows, analysis
|
|
35
|
-
- generating structured report of customer use-cases on the platform
|
|
36
|
-
- generating formula fields suggestions
|
|
37
|
-
- Also: user asks what the assistant can do, greets assistant
|
|
38
|
-
|
|
39
|
-
OUT_OF_SCOPE: Request is for DATA analytics - aggregating extracted data, generating charts/plots from document data, summarizing line items/amounts across documents, creating files unrelated to Rossum debugging. Even if it mentions Rossum annotations, if the goal is data aggregation/visualization, it's OUT_OF_SCOPE.
|
|
40
|
-
|
|
41
|
-
Examples:
|
|
42
|
-
- "Investigate errors with document splitting on queue X" → IN_SCOPE (debugging)
|
|
43
|
-
- "Aggregate line item amounts and generate a bar chart" → OUT_OF_SCOPE (data analytics)
|
|
44
|
-
- "Create a markdown saying hello" → OUT_OF_SCOPE (generic file creation)
|
|
45
|
-
|
|
46
|
-
Respond with exactly one word: IN_SCOPE or OUT_OF_SCOPE
|
|
47
|
-
|
|
48
|
-
User request: {message}"""
|
|
49
|
-
|
|
50
|
-
CLASSIFIER_MAX_TOKENS = 10
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
class RequestScope(Enum):
|
|
54
|
-
"""Classification result for a user request."""
|
|
55
|
-
|
|
56
|
-
IN_SCOPE = "IN_SCOPE"
|
|
57
|
-
OUT_OF_SCOPE = "OUT_OF_SCOPE"
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
@dataclass
|
|
61
|
-
class ClassificationResult:
|
|
62
|
-
"""Result of request classification."""
|
|
63
|
-
|
|
64
|
-
scope: RequestScope
|
|
65
|
-
raw_response: str
|
|
66
|
-
input_tokens: int = 0
|
|
67
|
-
output_tokens: int = 0
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
@dataclass
|
|
71
|
-
class RejectionResult:
|
|
72
|
-
"""Result of rejection response generation."""
|
|
73
|
-
|
|
74
|
-
response: str
|
|
75
|
-
input_tokens: int = 0
|
|
76
|
-
output_tokens: int = 0
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
REJECTION_PROMPT = """You are an expert Rossum platform specialist. The user made a request that is outside your scope.
|
|
80
|
-
|
|
81
|
-
I can help with:
|
|
82
|
-
- Analyzing and debugging hooks, extensions, and workflows
|
|
83
|
-
- Documenting queue configurations
|
|
84
|
-
- Investigating processing errors
|
|
85
|
-
- Configuring automation
|
|
86
|
-
|
|
87
|
-
The user asked: {message}
|
|
88
|
-
|
|
89
|
-
Write a brief, helpful response that:
|
|
90
|
-
1. Politely explains this is outside your Rossum platform expertise
|
|
91
|
-
2. Briefly mentions 2-3 relevant things you CAN help with from the capabilities above
|
|
92
|
-
3. Asks if they have any Rossum-related questions
|
|
93
|
-
|
|
94
|
-
Keep it concise (3-4 sentences max). Be friendly, not robotic."""
|
|
95
|
-
|
|
96
|
-
REJECTION_MAX_TOKENS = 300
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
def generate_rejection_response(client: AnthropicBedrock, message: str) -> RejectionResult:
|
|
100
|
-
"""Generate a contextual rejection response for out-of-scope requests."""
|
|
101
|
-
prompt = REJECTION_PROMPT.format(message=message)
|
|
102
|
-
try:
|
|
103
|
-
response = client.messages.create(
|
|
104
|
-
model=get_small_model_id(), max_tokens=REJECTION_MAX_TOKENS, messages=[{"role": "user", "content": prompt}]
|
|
105
|
-
)
|
|
106
|
-
text = response.content[0].text.strip() if response.content else _fallback_response()
|
|
107
|
-
return RejectionResult(
|
|
108
|
-
response=text, input_tokens=response.usage.input_tokens, output_tokens=response.usage.output_tokens
|
|
109
|
-
)
|
|
110
|
-
except Exception as e:
|
|
111
|
-
logger.warning(f"Rejection response generation failed: {e}")
|
|
112
|
-
return RejectionResult(response=_fallback_response())
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
def _fallback_response() -> str:
|
|
116
|
-
return (
|
|
117
|
-
"I'm an expert Rossum platform specialist focused on document processing workflows. "
|
|
118
|
-
"Your request appears to be outside my area of expertise. "
|
|
119
|
-
"I can help with analyzing hooks, debugging extensions, documenting queue configurations, "
|
|
120
|
-
"and configuring automation workflows. Do you have any Rossum-related questions?"
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
def classify_request(client: AnthropicBedrock, message: str) -> ClassificationResult:
|
|
125
|
-
"""Classify whether a user request is within scope.
|
|
126
|
-
|
|
127
|
-
Uses a fast, cheap model (Haiku) with minimal tokens to quickly determine if the request should be processed by the main agent.
|
|
128
|
-
"""
|
|
129
|
-
prompt = CLASSIFIER_PROMPT.format(message=message)
|
|
130
|
-
|
|
131
|
-
try:
|
|
132
|
-
response = client.messages.create(
|
|
133
|
-
model=get_small_model_id(),
|
|
134
|
-
max_tokens=CLASSIFIER_MAX_TOKENS,
|
|
135
|
-
messages=[{"role": "user", "content": prompt}],
|
|
136
|
-
)
|
|
137
|
-
|
|
138
|
-
raw_response = response.content[0].text.strip().upper() if response.content else ""
|
|
139
|
-
|
|
140
|
-
scope = RequestScope.OUT_OF_SCOPE if "OUT_OF_SCOPE" in raw_response else RequestScope.IN_SCOPE
|
|
141
|
-
|
|
142
|
-
logger.debug(f"Request classified as {scope.value}: {message[:50]}...")
|
|
143
|
-
return ClassificationResult(
|
|
144
|
-
scope=scope,
|
|
145
|
-
raw_response=raw_response,
|
|
146
|
-
input_tokens=response.usage.input_tokens,
|
|
147
|
-
output_tokens=response.usage.output_tokens,
|
|
148
|
-
)
|
|
149
|
-
|
|
150
|
-
except Exception as e:
|
|
151
|
-
logger.warning(f"Classification failed, defaulting to IN_SCOPE: {e}")
|
|
152
|
-
return ClassificationResult(scope=RequestScope.IN_SCOPE, raw_response=f"error: {e}")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|