quraite 0.1.3__py3-none-any.whl → 0.1.4__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.
- quraite/__init__.py +4 -0
- quraite/adapters/agno_adapter.py +50 -92
- quraite/adapters/base.py +26 -76
- quraite/adapters/bedrock_agents_adapter.py +23 -76
- quraite/adapters/flowise_adapter.py +31 -72
- quraite/adapters/google_adk_adapter.py +28 -94
- quraite/adapters/http_adapter.py +28 -44
- quraite/adapters/langchain_adapter.py +51 -118
- quraite/adapters/langchain_server_adapter.py +37 -89
- quraite/adapters/langflow_adapter.py +15 -60
- quraite/adapters/n8n_adapter.py +19 -63
- quraite/adapters/openai_agents_adapter.py +35 -59
- quraite/adapters/pydantic_ai_adapter.py +27 -97
- quraite/adapters/smolagents_adapter.py +21 -82
- quraite/constants/framework.py +14 -0
- quraite/schema/__init__.py +4 -0
- quraite/schema/invoke.py +46 -0
- quraite/schema/message.py +20 -21
- quraite/serve/__init__.py +4 -0
- quraite/serve/cloudflared.py +3 -2
- quraite/serve/server.py +305 -0
- quraite/tracing/__init__.py +8 -5
- quraite/tracing/constants.py +0 -14
- quraite/tracing/setup.py +129 -0
- quraite/tracing/span_exporter.py +6 -6
- quraite/tracing/span_processor.py +6 -7
- quraite/tracing/tool_extractors.py +1 -1
- quraite/tracing/trace.py +36 -24
- quraite/utils/json_utils.py +2 -2
- {quraite-0.1.3.dist-info → quraite-0.1.4.dist-info}/METADATA +54 -62
- quraite-0.1.4.dist-info/RECORD +37 -0
- quraite/schema/response.py +0 -16
- quraite/serve/local_agent.py +0 -361
- quraite/traces/traces_adk_openinference.json +0 -379
- quraite/traces/traces_agno_multi_agent.json +0 -669
- quraite/traces/traces_agno_openinference.json +0 -321
- quraite/traces/traces_crewai_openinference.json +0 -155
- quraite/traces/traces_langgraph_openinference.json +0 -349
- quraite/traces/traces_langgraph_openinference_multi_agent.json +0 -2705
- quraite/traces/traces_langgraph_traceloop.json +0 -510
- quraite/traces/traces_openai_agents_multi_agent_1.json +0 -402
- quraite/traces/traces_openai_agents_openinference.json +0 -341
- quraite/traces/traces_pydantic_openinference.json +0 -286
- quraite/traces/traces_pydantic_openinference_multi_agent_1.json +0 -399
- quraite/traces/traces_pydantic_openinference_multi_agent_2.json +0 -398
- quraite/traces/traces_smol_agents_openinference.json +0 -397
- quraite/traces/traces_smol_agents_tool_calling_openinference.json +0 -704
- quraite-0.1.3.dist-info/RECORD +0 -49
- {quraite-0.1.3.dist-info → quraite-0.1.4.dist-info}/WHEEL +0 -0
quraite/__init__.py
CHANGED
quraite/adapters/agno_adapter.py
CHANGED
|
@@ -1,135 +1,93 @@
|
|
|
1
|
-
|
|
1
|
+
import uuid
|
|
2
2
|
|
|
3
3
|
from agno.agent import Agent
|
|
4
4
|
from agno.team import Team
|
|
5
|
+
from openinference.instrumentation.agno.utils import _AGNO_PARENT_NODE_CONTEXT_KEY
|
|
6
|
+
from opentelemetry import context as context_api
|
|
5
7
|
from opentelemetry.trace import TracerProvider
|
|
6
8
|
|
|
7
9
|
from quraite.adapters.base import BaseAdapter
|
|
10
|
+
from quraite.constants.framework import Framework
|
|
8
11
|
from quraite.logger import get_logger
|
|
9
|
-
from quraite.schema.
|
|
10
|
-
from quraite.schema.response import AgentInvocationResponse
|
|
11
|
-
from quraite.tracing.constants import Framework
|
|
12
|
+
from quraite.schema.invoke import InvokeInput, InvokeOutput
|
|
12
13
|
from quraite.tracing.trace import AgentSpan, AgentTrace
|
|
13
14
|
|
|
14
15
|
logger = get_logger(__name__)
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
class AgnoAdapter(BaseAdapter):
|
|
18
|
-
"""
|
|
19
|
-
Agno adapter wrapper that converts any Agno agent or team
|
|
20
|
-
to a standardized callable interface (invoke) and converts the output to List[AgentMessage].
|
|
21
|
-
|
|
22
|
-
This class wraps any Agno Agent or Team and provides:
|
|
23
|
-
- Asynchronous invocation via ainvoke()
|
|
24
|
-
- Automatic conversion to List[AgentMessage] format
|
|
25
|
-
- Access to message history and traces
|
|
26
|
-
- Support for both single agents and multi-agent teams
|
|
27
|
-
"""
|
|
19
|
+
"""Agno adapter for Agent and Team (requires tracing)."""
|
|
28
20
|
|
|
29
21
|
def __init__(
|
|
30
22
|
self,
|
|
31
|
-
agent:
|
|
23
|
+
agent: Agent | Team,
|
|
24
|
+
*,
|
|
25
|
+
tracer_provider: TracerProvider | None = None,
|
|
32
26
|
agent_name: str = "Agno Agent",
|
|
33
|
-
tracer_provider: TracerProvider = None,
|
|
34
27
|
):
|
|
35
28
|
"""
|
|
36
|
-
Initialize
|
|
29
|
+
Initialize Agno adapter.
|
|
37
30
|
|
|
38
31
|
Args:
|
|
39
|
-
agent:
|
|
40
|
-
|
|
41
|
-
|
|
32
|
+
agent: Agno Agent or Team instance
|
|
33
|
+
tracer_provider: TracerProvider from setup_tracing() (required)
|
|
34
|
+
agent_name: Agent name for metadata
|
|
42
35
|
"""
|
|
43
|
-
|
|
44
|
-
|
|
36
|
+
if tracer_provider is None:
|
|
37
|
+
raise ValueError(
|
|
38
|
+
"Agno adapter requires tracing. Use setup_tracing([Framework.AGNO]) first."
|
|
39
|
+
)
|
|
45
40
|
|
|
46
|
-
self.agent:
|
|
41
|
+
self.agent: Agent | Team = agent
|
|
47
42
|
self.agent_name = agent_name
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
bool(tracer_provider),
|
|
51
|
-
)
|
|
52
|
-
|
|
53
|
-
def _prepare_input(self, input: List[AgentMessage]) -> str:
|
|
54
|
-
"""
|
|
55
|
-
Prepare input for Agno agent from List[AgentMessage].
|
|
56
|
-
|
|
57
|
-
Args:
|
|
58
|
-
input: List[AgentMessage] containing user_message
|
|
59
|
-
|
|
60
|
-
Returns:
|
|
61
|
-
str: User message text
|
|
62
|
-
"""
|
|
63
|
-
logger.debug("Preparing input from %d messages", len(input))
|
|
64
|
-
if not input or input[-1].role != "user":
|
|
65
|
-
logger.error("Invalid input: no user message found")
|
|
66
|
-
raise ValueError("No user message found in the input")
|
|
67
|
-
|
|
68
|
-
last_user_message = input[-1]
|
|
69
|
-
if not last_user_message.content:
|
|
70
|
-
logger.error("User message has no content")
|
|
71
|
-
raise ValueError("User message has no content")
|
|
72
|
-
|
|
73
|
-
text_content = None
|
|
74
|
-
for content_item in last_user_message.content:
|
|
75
|
-
if content_item.type == "text" and content_item.text:
|
|
76
|
-
text_content = content_item.text
|
|
77
|
-
break
|
|
78
|
-
|
|
79
|
-
if not text_content:
|
|
80
|
-
logger.error("No text content found in user message")
|
|
81
|
-
raise ValueError("No text content found in user message")
|
|
43
|
+
self._init_tracer(tracer_provider)
|
|
44
|
+
logger.info("AgnoAdapter initialized")
|
|
82
45
|
|
|
83
|
-
|
|
84
|
-
return
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
self,
|
|
88
|
-
input: List[AgentMessage],
|
|
89
|
-
session_id: Union[str, None] = None,
|
|
90
|
-
) -> AgentInvocationResponse:
|
|
91
|
-
"""
|
|
92
|
-
Asynchronous invocation method - invokes the Agno agent/team with tracing
|
|
93
|
-
|
|
94
|
-
Args:
|
|
95
|
-
input: List[AgentMessage] containing user_message
|
|
96
|
-
session_id: Optional conversation ID for maintaining context
|
|
97
|
-
|
|
98
|
-
Returns:
|
|
99
|
-
AgentInvocationResponse - response containing agent trace, trajectory, and final response.
|
|
100
|
-
"""
|
|
101
|
-
logger.info(
|
|
102
|
-
"ainvoke called (session_id=%s, input_messages=%d)", session_id, len(input)
|
|
103
|
-
)
|
|
104
|
-
agent_input = self._prepare_input(input)
|
|
105
|
-
session_id = session_id or "default"
|
|
46
|
+
async def ainvoke(self, input: InvokeInput) -> InvokeOutput:
|
|
47
|
+
"""Invoke Agno agent and return response with trace."""
|
|
48
|
+
logger.info("ainvoke called (session_id=%s)", input.session_id)
|
|
49
|
+
session_id = input.session_id or str(uuid.uuid4())
|
|
106
50
|
|
|
107
51
|
try:
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
except ValueError:
|
|
112
|
-
logger.exception("Invalid input during ainvoke")
|
|
113
|
-
raise
|
|
52
|
+
return await self._ainvoke_with_tracing(
|
|
53
|
+
input.user_message_str(), session_id
|
|
54
|
+
)
|
|
114
55
|
except Exception as e:
|
|
115
|
-
logger.exception("
|
|
56
|
+
logger.exception("Error invoking Agno agent")
|
|
116
57
|
raise RuntimeError(f"Error invoking Agno agent: {e}") from e
|
|
117
58
|
|
|
118
59
|
async def _ainvoke_with_tracing(
|
|
119
60
|
self,
|
|
120
|
-
|
|
61
|
+
user_message: str,
|
|
121
62
|
session_id: str,
|
|
122
|
-
) ->
|
|
63
|
+
) -> InvokeOutput:
|
|
123
64
|
"""Execute ainvoke with tracing enabled."""
|
|
124
65
|
with self.tracer.start_as_current_span("agno_invocation") as span:
|
|
66
|
+
# Workaround: openinference-instrumentation-agno>=0.1.25 forces a new root
|
|
67
|
+
# trace for Team instances unless _AGNO_PARENT_NODE_CONTEXT_KEY is set.
|
|
68
|
+
# This ensures child spans inherit our trace_id.
|
|
69
|
+
# See: https://github.com/Arize-ai/openinference/pull/2533
|
|
70
|
+
#
|
|
71
|
+
# Context API notes:
|
|
72
|
+
# - set_value() creates a new Context with the key-value pair (doesn't activate it)
|
|
73
|
+
# - attach() makes the context active and returns a token (restore point)
|
|
74
|
+
# - detach(token) restores the previous context (cleanup)
|
|
75
|
+
ctx = context_api.set_value(
|
|
76
|
+
_AGNO_PARENT_NODE_CONTEXT_KEY,
|
|
77
|
+
format(span.get_span_context().span_id, '016x')
|
|
78
|
+
)
|
|
79
|
+
token = context_api.attach(ctx)
|
|
125
80
|
trace_id = span.get_span_context().trace_id
|
|
81
|
+
|
|
126
82
|
logger.debug(
|
|
127
83
|
"Starting traced invocation (session_id=%s) with trace_id=%s",
|
|
128
84
|
session_id,
|
|
129
85
|
trace_id,
|
|
130
86
|
)
|
|
131
|
-
|
|
132
|
-
|
|
87
|
+
try:
|
|
88
|
+
await self.agent.arun(user_message, session_id=session_id)
|
|
89
|
+
finally:
|
|
90
|
+
context_api.detach(token)
|
|
133
91
|
|
|
134
92
|
# Get trace spans
|
|
135
93
|
trace_readable_spans = self.quraite_span_exporter.get_spans_by_trace_id(
|
|
@@ -151,7 +109,7 @@ class AgnoAdapter(BaseAdapter):
|
|
|
151
109
|
else:
|
|
152
110
|
logger.warning("No spans found for trace_id=%s", trace_id)
|
|
153
111
|
|
|
154
|
-
return
|
|
112
|
+
return InvokeOutput(
|
|
155
113
|
agent_trace=agent_trace,
|
|
156
114
|
agent_trajectory=agent_trace.to_agent_trajectory(framework=Framework.AGNO),
|
|
157
115
|
)
|
quraite/adapters/base.py
CHANGED
|
@@ -1,49 +1,28 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
|
-
from typing import Any, List, Optional, Union
|
|
3
2
|
|
|
3
|
+
from opentelemetry.sdk.trace import TracerProvider as SDKTracerProvider
|
|
4
4
|
from opentelemetry.trace import Tracer, TracerProvider
|
|
5
5
|
|
|
6
|
-
from quraite.
|
|
7
|
-
from quraite.schema.
|
|
6
|
+
from quraite.logger import get_logger
|
|
7
|
+
from quraite.schema.invoke import InvokeInput, InvokeOutput
|
|
8
|
+
from quraite.schema.message import AssistantMessage, MessageContentText
|
|
8
9
|
from quraite.tracing.constants import QURAITE_TRACER_NAME
|
|
9
|
-
from quraite.tracing.span_exporter import
|
|
10
|
-
from quraite.tracing.span_processor import
|
|
10
|
+
from quraite.tracing.span_exporter import QuraiteSpanExporter
|
|
11
|
+
from quraite.tracing.span_processor import QuraiteSpanProcessor
|
|
11
12
|
|
|
13
|
+
logger = get_logger(__name__)
|
|
12
14
|
|
|
13
|
-
class BaseAdapter(ABC):
|
|
14
|
-
"""
|
|
15
|
-
Abstract base class for all adapter providers.
|
|
16
|
-
|
|
17
|
-
Subclasses must implement the asynchronous adapter method,
|
|
18
|
-
which take List[Message] and return agent Message outputs.
|
|
19
|
-
|
|
20
|
-
Methods:
|
|
21
|
-
ainvoke: Asynchronously invoke an agent call for the provided input.
|
|
22
|
-
"""
|
|
23
15
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
quraite_span_exporter: Optional[QuraiteInMemorySpanExporter] = None
|
|
16
|
+
class BaseAdapter(ABC):
|
|
17
|
+
"""Base adapter for all framework adapters."""
|
|
27
18
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
required: bool = False,
|
|
32
|
-
) -> None:
|
|
33
|
-
"""
|
|
34
|
-
Initialize tracing components from a TracerProvider.
|
|
19
|
+
tracer_provider: TracerProvider | None = None
|
|
20
|
+
tracer: Tracer | None = None
|
|
21
|
+
quraite_span_exporter: QuraiteSpanExporter = QuraiteSpanExporter()
|
|
35
22
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
span_exporter: SpanExporter for exporting spans
|
|
39
|
-
required: If True, raises ValueError when tracer_provider is None
|
|
40
|
-
"""
|
|
23
|
+
def _init_tracer(self, tracer_provider: SDKTracerProvider | None) -> None:
|
|
24
|
+
"""Initialize tracer components from TracerProvider."""
|
|
41
25
|
if tracer_provider is None:
|
|
42
|
-
if required:
|
|
43
|
-
raise ValueError(
|
|
44
|
-
"tracer_provider is required. "
|
|
45
|
-
"Please provide a TracerProvider instance."
|
|
46
|
-
)
|
|
47
26
|
return
|
|
48
27
|
|
|
49
28
|
self.tracer_provider = tracer_provider
|
|
@@ -54,7 +33,7 @@ class BaseAdapter(ABC):
|
|
|
54
33
|
(
|
|
55
34
|
processor.span_exporter
|
|
56
35
|
for processor in tracer_provider._active_span_processor._span_processors
|
|
57
|
-
if isinstance(processor,
|
|
36
|
+
if isinstance(processor, QuraiteSpanProcessor)
|
|
58
37
|
),
|
|
59
38
|
None,
|
|
60
39
|
)
|
|
@@ -62,7 +41,7 @@ class BaseAdapter(ABC):
|
|
|
62
41
|
if quraite_span_exporter is None:
|
|
63
42
|
raise ValueError(
|
|
64
43
|
"Quraite span exporter not found. "
|
|
65
|
-
"
|
|
44
|
+
"Use setup_tracing() to configure tracing properly."
|
|
66
45
|
)
|
|
67
46
|
|
|
68
47
|
self.quraite_span_exporter = quraite_span_exporter
|
|
@@ -70,54 +49,25 @@ class BaseAdapter(ABC):
|
|
|
70
49
|
@abstractmethod
|
|
71
50
|
async def ainvoke(
|
|
72
51
|
self,
|
|
73
|
-
input:
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
"""
|
|
77
|
-
Asynchronously invoke the agent with the given input.
|
|
78
|
-
|
|
79
|
-
Args:
|
|
80
|
-
input (List[AgentMessage]): List of AgentMessage objects.
|
|
81
|
-
session_id (str or None): ID for conversation thread/context.
|
|
82
|
-
|
|
83
|
-
Returns:
|
|
84
|
-
AgentInvocationResponse: Response containing agent trace, trajectory, and final response.
|
|
85
|
-
|
|
86
|
-
Raises:
|
|
87
|
-
NotImplementedError: If not implemented by subclass.
|
|
88
|
-
"""
|
|
52
|
+
input: InvokeInput,
|
|
53
|
+
) -> InvokeOutput:
|
|
54
|
+
"""Invoke agent with input and return response."""
|
|
89
55
|
raise NotImplementedError("Not implemented")
|
|
90
56
|
|
|
91
57
|
|
|
92
58
|
class DummyAdapter(BaseAdapter):
|
|
93
|
-
"""
|
|
94
|
-
A dummy implementation of BaseAdapter, used mainly for testing and scaffolding.
|
|
95
|
-
Always returns a fixed dummy response.
|
|
96
|
-
|
|
97
|
-
Methods:
|
|
98
|
-
ainvoke: Returns a static assistant message asynchronously.
|
|
99
|
-
"""
|
|
59
|
+
"""Dummy adapter for testing."""
|
|
100
60
|
|
|
101
61
|
async def ainvoke(
|
|
102
62
|
self,
|
|
103
|
-
input:
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
Asynchronously returns a dummy assistant response.
|
|
108
|
-
|
|
109
|
-
Args:
|
|
110
|
-
input: Ignored.
|
|
111
|
-
session_id: Ignored.
|
|
112
|
-
|
|
113
|
-
Returns:
|
|
114
|
-
AgentInvocationResponse: Response containing agent trace, trajectory, and final response.
|
|
115
|
-
"""
|
|
116
|
-
|
|
117
|
-
return AgentInvocationResponse(
|
|
63
|
+
input: InvokeInput,
|
|
64
|
+
) -> InvokeOutput:
|
|
65
|
+
"""Returns dummy response."""
|
|
66
|
+
return InvokeOutput(
|
|
118
67
|
agent_trajectory=[
|
|
68
|
+
input.user_message,
|
|
119
69
|
AssistantMessage(
|
|
120
70
|
content=[MessageContentText(text="Dummy response")],
|
|
121
|
-
)
|
|
71
|
+
),
|
|
122
72
|
]
|
|
123
73
|
)
|
|
@@ -7,13 +7,14 @@ import asyncio
|
|
|
7
7
|
import json
|
|
8
8
|
import os
|
|
9
9
|
import uuid
|
|
10
|
-
from typing import Any
|
|
10
|
+
from typing import Any
|
|
11
11
|
|
|
12
12
|
import boto3
|
|
13
13
|
from botocore.exceptions import ClientError
|
|
14
14
|
|
|
15
15
|
from quraite.adapters.base import BaseAdapter
|
|
16
16
|
from quraite.logger import get_logger
|
|
17
|
+
from quraite.schema.invoke import InvokeInput, InvokeOutput
|
|
17
18
|
from quraite.schema.message import (
|
|
18
19
|
AgentMessage,
|
|
19
20
|
AssistantMessage,
|
|
@@ -21,7 +22,6 @@ from quraite.schema.message import (
|
|
|
21
22
|
ToolCall,
|
|
22
23
|
ToolMessage,
|
|
23
24
|
)
|
|
24
|
-
from quraite.schema.response import AgentInvocationResponse
|
|
25
25
|
|
|
26
26
|
logger = get_logger(__name__)
|
|
27
27
|
|
|
@@ -38,12 +38,12 @@ class BedrockAgentsAdapter(BaseAdapter):
|
|
|
38
38
|
|
|
39
39
|
def __init__(
|
|
40
40
|
self,
|
|
41
|
-
aws_access_key_id:
|
|
42
|
-
aws_secret_access_key:
|
|
43
|
-
aws_session_token:
|
|
44
|
-
agent_id:
|
|
45
|
-
agent_alias_id:
|
|
46
|
-
region_name:
|
|
41
|
+
aws_access_key_id: str | None = None,
|
|
42
|
+
aws_secret_access_key: str | None = None,
|
|
43
|
+
aws_session_token: str | None = None,
|
|
44
|
+
agent_id: str | None = None,
|
|
45
|
+
agent_alias_id: str | None = None,
|
|
46
|
+
region_name: str | None = None,
|
|
47
47
|
agent_name: str = "Bedrock Agent",
|
|
48
48
|
):
|
|
49
49
|
"""
|
|
@@ -92,8 +92,8 @@ class BedrockAgentsAdapter(BaseAdapter):
|
|
|
92
92
|
|
|
93
93
|
def _convert_bedrock_traces_to_messages(
|
|
94
94
|
self,
|
|
95
|
-
traces:
|
|
96
|
-
) ->
|
|
95
|
+
traces: list[dict[str, Any]],
|
|
96
|
+
) -> list[AgentMessage]:
|
|
97
97
|
logger.debug("Converting %d Bedrock trace events to messages", len(traces))
|
|
98
98
|
if not traces:
|
|
99
99
|
return []
|
|
@@ -121,7 +121,7 @@ class BedrockAgentsAdapter(BaseAdapter):
|
|
|
121
121
|
try:
|
|
122
122
|
parsed_content = json.loads(raw_response_content)
|
|
123
123
|
contents = parsed_content.get("content", [])
|
|
124
|
-
except (json.JSONDecodeError, KeyError, ValueError)
|
|
124
|
+
except (json.JSONDecodeError, KeyError, ValueError):
|
|
125
125
|
logger.exception("Error parsing Bedrock raw response content")
|
|
126
126
|
|
|
127
127
|
if not contents:
|
|
@@ -223,27 +223,7 @@ class BedrockAgentsAdapter(BaseAdapter):
|
|
|
223
223
|
logger.info("Converted Bedrock traces into %d messages", len(messages))
|
|
224
224
|
return messages
|
|
225
225
|
|
|
226
|
-
def
|
|
227
|
-
"""Extract user message from List[AgentMessage]."""
|
|
228
|
-
logger.debug("Preparing Bedrock input from %d messages", len(input_data))
|
|
229
|
-
last_user_message = input_data[-1]
|
|
230
|
-
if last_user_message.role != "user":
|
|
231
|
-
logger.error("Last message is not from user")
|
|
232
|
-
return ""
|
|
233
|
-
# Check if content list is not empty and has text
|
|
234
|
-
if not last_user_message.content:
|
|
235
|
-
logger.error("User message has no content")
|
|
236
|
-
raise ValueError("User message has no content")
|
|
237
|
-
# Find the first text content item
|
|
238
|
-
for content_item in last_user_message.content:
|
|
239
|
-
if content_item.type == "text" and content_item.text:
|
|
240
|
-
logger.debug(
|
|
241
|
-
"Prepared Bedrock input (text_length=%d)", len(content_item.text)
|
|
242
|
-
)
|
|
243
|
-
return content_item.text
|
|
244
|
-
raise ValueError("No text content found in user message")
|
|
245
|
-
|
|
246
|
-
def _run_agent(self, session_id: str, prompt: str) -> List[Dict]:
|
|
226
|
+
def _run_agent(self, session_id: str, prompt: str) -> tuple[str, list[dict]]:
|
|
247
227
|
"""
|
|
248
228
|
Run the Bedrock agent and collect response and traces.
|
|
249
229
|
|
|
@@ -283,7 +263,7 @@ class BedrockAgentsAdapter(BaseAdapter):
|
|
|
283
263
|
|
|
284
264
|
except ClientError:
|
|
285
265
|
logger.exception("Error invoking Bedrock agent via Bedrock runtime")
|
|
286
|
-
|
|
266
|
+
raise
|
|
287
267
|
logger.info(
|
|
288
268
|
"Bedrock agent invocation succeeded (session_id=%s, trace_events=%d)",
|
|
289
269
|
session_id,
|
|
@@ -291,53 +271,20 @@ class BedrockAgentsAdapter(BaseAdapter):
|
|
|
291
271
|
)
|
|
292
272
|
return agent_answer, traces
|
|
293
273
|
|
|
294
|
-
async def ainvoke(
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
session_id
|
|
298
|
-
) -> AgentInvocationResponse:
|
|
299
|
-
"""Asynchronous invocation method - invokes the Bedrock agent and converts to List[AgentMessage].
|
|
300
|
-
|
|
301
|
-
Args:
|
|
302
|
-
input: List of AgentMessage objects
|
|
303
|
-
session_id: Unique session identifier
|
|
304
|
-
|
|
305
|
-
Returns:
|
|
306
|
-
AgentInvocationResponse - response containing agent trace, trajectory, and final response.
|
|
307
|
-
"""
|
|
308
|
-
logger.info(
|
|
309
|
-
"Bedrock ainvoke called (session_id=%s, input_messages=%d)",
|
|
310
|
-
session_id,
|
|
311
|
-
len(input),
|
|
312
|
-
)
|
|
313
|
-
agent_input = self._prepare_input(input)
|
|
314
|
-
session_id = session_id or str(uuid.uuid4())
|
|
274
|
+
async def ainvoke(self, input: InvokeInput) -> InvokeOutput:
|
|
275
|
+
"""Invoke Bedrock agent and return response."""
|
|
276
|
+
logger.info("Bedrock ainvoke called (session_id=%s)", input.session_id)
|
|
277
|
+
session_id = input.session_id or str(uuid.uuid4())
|
|
315
278
|
|
|
316
279
|
try:
|
|
317
|
-
# Run the synchronous _run_agent in a thread pool to avoid blocking
|
|
318
280
|
_, traces = await asyncio.to_thread(
|
|
319
|
-
self._run_agent, session_id,
|
|
320
|
-
)
|
|
321
|
-
logger.debug(
|
|
322
|
-
"Bedrock agent run returned %d trace events for session_id=%s",
|
|
323
|
-
len(traces),
|
|
324
|
-
session_id,
|
|
281
|
+
self._run_agent, session_id, input.user_message_str()
|
|
325
282
|
)
|
|
326
|
-
except (ClientError, ValueError, KeyError, json.JSONDecodeError):
|
|
327
|
-
logger.exception("Error invoking Bedrock agent")
|
|
328
|
-
return AgentInvocationResponse()
|
|
329
|
-
|
|
330
|
-
try:
|
|
331
283
|
agent_trajectory = self._convert_bedrock_traces_to_messages(traces)
|
|
332
284
|
logger.info(
|
|
333
|
-
"Bedrock agent produced %d trajectory messages",
|
|
334
|
-
len(agent_trajectory),
|
|
335
|
-
)
|
|
336
|
-
|
|
337
|
-
return AgentInvocationResponse(
|
|
338
|
-
agent_trajectory=agent_trajectory,
|
|
285
|
+
"Bedrock agent produced %d trajectory messages", len(agent_trajectory)
|
|
339
286
|
)
|
|
340
|
-
|
|
341
|
-
except
|
|
342
|
-
logger.exception("Error
|
|
343
|
-
return
|
|
287
|
+
return InvokeOutput(agent_trajectory=agent_trajectory)
|
|
288
|
+
except Exception:
|
|
289
|
+
logger.exception("Error invoking Bedrock agent")
|
|
290
|
+
return InvokeOutput()
|