agentx-python 0.4.3__tar.gz → 0.4.9__tar.gz

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.
Files changed (37) hide show
  1. {agentx_python-0.4.3/agentx_python.egg-info → agentx_python-0.4.9}/PKG-INFO +21 -68
  2. agentx_python-0.4.9/README.md +112 -0
  3. {agentx_python-0.4.3 → agentx_python-0.4.9}/agentx/agentx.py +27 -14
  4. agentx_python-0.4.9/agentx/evaluations/__init__.py +3 -0
  5. agentx_python-0.4.9/agentx/evaluations/_term.py +69 -0
  6. agentx_python-0.4.9/agentx/evaluations/adapters/__init__.py +5 -0
  7. agentx_python-0.4.9/agentx/evaluations/adapters/http_endpoint.py +69 -0
  8. agentx_python-0.4.9/agentx/evaluations/adapters/precomputed.py +33 -0
  9. agentx_python-0.4.9/agentx/evaluations/adapters/raw.py +35 -0
  10. agentx_python-0.4.9/agentx/evaluations/client.py +183 -0
  11. agentx_python-0.4.9/agentx/evaluations/datasets.py +220 -0
  12. agentx_python-0.4.9/agentx/evaluations/models.py +294 -0
  13. agentx_python-0.4.9/agentx/evaluations/redaction.py +45 -0
  14. agentx_python-0.4.9/agentx/evaluations/reporting.py +146 -0
  15. agentx_python-0.4.9/agentx/evaluations/results.py +109 -0
  16. agentx_python-0.4.9/agentx/evaluations/runner.py +293 -0
  17. agentx_python-0.4.9/agentx/evaluations/tracing.py +54 -0
  18. {agentx_python-0.4.3 → agentx_python-0.4.9}/agentx/resources/agent.py +15 -5
  19. {agentx_python-0.4.3 → agentx_python-0.4.9}/agentx/resources/conversation.py +5 -5
  20. {agentx_python-0.4.3 → agentx_python-0.4.9}/agentx/resources/workforce.py +7 -7
  21. agentx_python-0.4.9/agentx/util.py +19 -0
  22. agentx_python-0.4.9/agentx/version.py +1 -0
  23. {agentx_python-0.4.3 → agentx_python-0.4.9/agentx_python.egg-info}/PKG-INFO +21 -68
  24. agentx_python-0.4.9/agentx_python.egg-info/SOURCES.txt +31 -0
  25. {agentx_python-0.4.3 → agentx_python-0.4.9}/setup.py +1 -1
  26. agentx_python-0.4.9/tests/test_integration.py +174 -0
  27. agentx_python-0.4.3/README.md +0 -159
  28. agentx_python-0.4.3/agentx/util.py +0 -5
  29. agentx_python-0.4.3/agentx/version.py +0 -1
  30. agentx_python-0.4.3/agentx_python.egg-info/SOURCES.txt +0 -16
  31. {agentx_python-0.4.3 → agentx_python-0.4.9}/LICENSE +0 -0
  32. {agentx_python-0.4.3 → agentx_python-0.4.9}/agentx/__init__.py +0 -0
  33. {agentx_python-0.4.3 → agentx_python-0.4.9}/agentx/resources/__init__.py +0 -0
  34. {agentx_python-0.4.3 → agentx_python-0.4.9}/agentx_python.egg-info/dependency_links.txt +0 -0
  35. {agentx_python-0.4.3 → agentx_python-0.4.9}/agentx_python.egg-info/requires.txt +0 -0
  36. {agentx_python-0.4.3 → agentx_python-0.4.9}/agentx_python.egg-info/top_level.txt +0 -0
  37. {agentx_python-0.4.3 → agentx_python-0.4.9}/setup.cfg +0 -0
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentx-python
3
- Version: 0.4.3
3
+ Version: 0.4.9
4
4
  Summary: Official Python SDK for AgentX (https://www.agentx.so/)
5
- Home-page: https://github.com/AgentX-ai/AgentX-python-sdk
5
+ Home-page: https://github.com/AgentX-ai/AgentX-python
6
6
  Author: Robin Wang and AgentX Team
7
7
  Author-email: contact@agentx.so
8
8
  Classifier: Programming Language :: Python :: 3
@@ -33,22 +33,22 @@ Dynamic: summary
33
33
 
34
34
  ---
35
35
 
36
- ## a fast way to build AI Agents and create agent workforce
36
+ ## A fast way to build AI Agents and create agent workforces
37
37
 
38
38
  The official AgentX Python SDK for [AgentX](https://www.agentx.so/)
39
39
 
40
- Why build AI agent with AgentX?
40
+ Why build AI agents with AgentX?
41
41
 
42
- - Simplicity, Agent - Conversation - Message structure.
43
- - Include chain-of-thoughts.
44
- - Choose from most open and closed sourced LLM vendors.
45
- - Built-in Voice(ASR, TTS), Image Gen, Document, CSV/excel tool, OCR, etc.
46
- - Support all running MCP (model context protocol).
47
- - Support RAG with built-in re-rank.
42
+ - Simplicity Agent Conversation Message structure.
43
+ - Chain-of-thought built in.
44
+ - Choose from most open and closed-source LLM vendors.
45
+ - Built-in Voice (ASR, TTS), Image Gen, Document, CSV/Excel, OCR, and more.
46
+ - Support for all running MCP (Model Context Protocol) servers.
47
+ - RAG with built-in re-rank.
48
48
  - Multi-agent workforce orchestration.
49
49
  - Multiple agents working together with a designated manager agent.
50
- - Cross LLM vendor, multi-agent orchestration.
51
- - A2A - agent to agent protocol
50
+ - Cross-LLM-vendor, multi-agent orchestration.
51
+ - A2A agent-to-agent protocol (coming soon)
52
52
 
53
53
  ## Installation
54
54
 
@@ -58,19 +58,14 @@ pip install --upgrade agentx-python
58
58
 
59
59
  ## Quick Start
60
60
 
61
- Get started with AgentX in just a few lines of code:
62
-
63
61
  ```python
64
62
  from agentx import AgentX
65
63
 
66
- # Initialize the client
67
64
  client = AgentX(api_key="your-api-key-here")
68
65
 
69
- # Get your agents
70
66
  agents = client.list_agents()
71
67
  print(f"You have {len(agents)} agents")
72
68
 
73
- # Start chatting with your first agent
74
69
  if agents:
75
70
  agent = agents[0]
76
71
  conversation = agent.new_conversation()
@@ -81,7 +76,7 @@ if agents:
81
76
  ## Usage
82
77
 
83
78
  Provide an `api_key` inline or set `AGENTX_API_KEY` as an environment variable.
84
- You can get an API key from https://app.agentx.so
79
+ Get your API key at https://app.agentx.so
85
80
 
86
81
  ### Agent
87
82
 
@@ -89,24 +84,15 @@ You can get an API key from https://app.agentx.so
89
84
  from agentx import AgentX
90
85
 
91
86
  client = AgentX(api_key="<your api key here>")
92
-
93
- # Get the list of agents you have
94
87
  print(client.list_agents())
95
88
  ```
96
89
 
97
90
  ### Conversation
98
91
 
99
- Each Conversation has `agents` and `users` tied to it.
100
-
101
92
  ```python
102
- # get agent
103
93
  my_agent = client.get_agent(id="<agent id here>")
104
94
 
105
- # Get the list of conversation from this agent
106
95
  existing_conversations = my_agent.list_conversations()
107
- print(existing_conversations)
108
-
109
- # Get the list of history messages from a conversation
110
96
  last_conversation = existing_conversations[-1]
111
97
  msgs = last_conversation.list_messages()
112
98
  print(msgs)
@@ -114,8 +100,6 @@ print(msgs)
114
100
 
115
101
  ### Chat
116
102
 
117
- A `chat` needs to happen in the conversation. You can do `stream` response too, default `False`.
118
-
119
103
  ```python
120
104
  a_conversation = my_agent.get_conversation(id="<conversation id here>")
121
105
 
@@ -124,65 +108,34 @@ for chunk in response:
124
108
  print(chunk)
125
109
  ```
126
110
 
127
- output looks like:
128
-
129
- ```
130
- text=None cot='The user is greeting and asking for my ' botId='xxx'
131
- text=None cot='name, which are casual, straightforward questions.' botId='xxx'
132
- text=None cot=' I can answer these directly' botId='xxx'
133
- text='Hello' cot=None botId='xxx'
134
- text='!' cot=None botId='xxx'
135
- text=' I' cot=None botId='xxx'
136
- text=' am' cot=None botId='xxx'
137
- text=' AgentX' cot=None botId='xxx'
138
- text=None cot=None botId='xxx'
139
- ```
140
-
141
- \*`cot` stands for chain-of-thoughts
111
+ \*`cot` stands for chain-of-thought
142
112
 
143
113
  ### Workforce
144
114
 
145
- A Workforce (team) consists of multiple agents working together with a designated manager agent.
146
-
147
115
  ```python
148
116
  from agentx import AgentX
149
117
 
150
118
  client = AgentX(api_key="<your api key here>")
151
119
 
152
- # Get the list of workforces/teams you have
153
120
  workforces = client.list_workforces()
154
- print(workforces)
155
-
156
- # Get a specific workforce
157
- workforce = workforces[0] # or any specific workforce
121
+ workforce = workforces[0]
158
122
  print(f"Workforce: {workforce.name}")
159
123
  print(f"Manager: {workforce.manager.name}")
160
124
  print(f"Agents: {[agent.name for agent in workforce.agents]}")
161
125
  ```
162
126
 
163
- #### Workforce Conversations
164
-
165
- ```python
166
- # Create a new conversation with the workforce
167
- conversation = workforce.new_conversation()
168
-
169
- # List all existing conversations for the workforce
170
- conversations = workforce.list_conversations()
171
- print(conversations)
172
- ```
173
-
174
127
  #### Chat with Workforce
175
128
 
176
- Chat with the entire workforce team and get streaming responses from all agents.
177
-
178
129
  ```python
179
- # Stream chat with the workforce
130
+ conversation = workforce.new_conversation()
180
131
  response = workforce.chat_stream(conversation.id, "How can you help me with this project?")
181
132
  for chunk in response:
182
133
  if chunk.text:
183
134
  print(chunk.text, end="")
184
- if chunk.cot:
185
- print(f" [COT: {chunk.cot}]")
186
135
  ```
187
136
 
188
- The workforce chat allows you to leverage multiple specialized agents working together to provide comprehensive responses to your queries.
137
+ ---
138
+
139
+ ## Custom Agent Evaluations
140
+
141
+ See **[EVALUATIONS.md](EVALUATIONS.md)** for full documentation — installation, dataset builder, framework examples (OpenAI, Anthropic, Google, LangChain, CrewAI, AutoGen, LlamaIndex, HTTP endpoints), and the complete API reference.
@@ -0,0 +1,112 @@
1
+ ![Logo](https://agentx-resources.s3.us-west-1.amazonaws.com/AgentX-logo-387x60.png)
2
+
3
+ [![PyPI version](https://img.shields.io/pypi/v/agentx-python)](https://pypi.org/project/agentx-python/)
4
+
5
+ ---
6
+
7
+ ## A fast way to build AI Agents and create agent workforces
8
+
9
+ The official AgentX Python SDK for [AgentX](https://www.agentx.so/)
10
+
11
+ Why build AI agents with AgentX?
12
+
13
+ - Simplicity — Agent → Conversation → Message structure.
14
+ - Chain-of-thought built in.
15
+ - Choose from most open and closed-source LLM vendors.
16
+ - Built-in Voice (ASR, TTS), Image Gen, Document, CSV/Excel, OCR, and more.
17
+ - Support for all running MCP (Model Context Protocol) servers.
18
+ - RAG with built-in re-rank.
19
+ - Multi-agent workforce orchestration.
20
+ - Multiple agents working together with a designated manager agent.
21
+ - Cross-LLM-vendor, multi-agent orchestration.
22
+ - A2A — agent-to-agent protocol (coming soon)
23
+
24
+ ## Installation
25
+
26
+ ```bash
27
+ pip install --upgrade agentx-python
28
+ ```
29
+
30
+ ## Quick Start
31
+
32
+ ```python
33
+ from agentx import AgentX
34
+
35
+ client = AgentX(api_key="your-api-key-here")
36
+
37
+ agents = client.list_agents()
38
+ print(f"You have {len(agents)} agents")
39
+
40
+ if agents:
41
+ agent = agents[0]
42
+ conversation = agent.new_conversation()
43
+ response = conversation.chat("Hello! What can you help me with?")
44
+ print(response)
45
+ ```
46
+
47
+ ## Usage
48
+
49
+ Provide an `api_key` inline or set `AGENTX_API_KEY` as an environment variable.
50
+ Get your API key at https://app.agentx.so
51
+
52
+ ### Agent
53
+
54
+ ```python
55
+ from agentx import AgentX
56
+
57
+ client = AgentX(api_key="<your api key here>")
58
+ print(client.list_agents())
59
+ ```
60
+
61
+ ### Conversation
62
+
63
+ ```python
64
+ my_agent = client.get_agent(id="<agent id here>")
65
+
66
+ existing_conversations = my_agent.list_conversations()
67
+ last_conversation = existing_conversations[-1]
68
+ msgs = last_conversation.list_messages()
69
+ print(msgs)
70
+ ```
71
+
72
+ ### Chat
73
+
74
+ ```python
75
+ a_conversation = my_agent.get_conversation(id="<conversation id here>")
76
+
77
+ response = a_conversation.chat_stream("Hello, what is your name?")
78
+ for chunk in response:
79
+ print(chunk)
80
+ ```
81
+
82
+ \*`cot` stands for chain-of-thought
83
+
84
+ ### Workforce
85
+
86
+ ```python
87
+ from agentx import AgentX
88
+
89
+ client = AgentX(api_key="<your api key here>")
90
+
91
+ workforces = client.list_workforces()
92
+ workforce = workforces[0]
93
+ print(f"Workforce: {workforce.name}")
94
+ print(f"Manager: {workforce.manager.name}")
95
+ print(f"Agents: {[agent.name for agent in workforce.agents]}")
96
+ ```
97
+
98
+ #### Chat with Workforce
99
+
100
+ ```python
101
+ conversation = workforce.new_conversation()
102
+ response = workforce.chat_stream(conversation.id, "How can you help me with this project?")
103
+ for chunk in response:
104
+ if chunk.text:
105
+ print(chunk.text, end="")
106
+ ```
107
+
108
+ ---
109
+
110
+ ## Custom Agent Evaluations
111
+
112
+ See **[EVALUATIONS.md](EVALUATIONS.md)** for full documentation — installation, dataset builder, framework examples (OpenAI, Anthropic, Google, LangChain, CrewAI, AutoGen, LlamaIndex, HTTP endpoints), and the complete API reference.
@@ -3,37 +3,50 @@ import requests
3
3
  import os
4
4
  import logging
5
5
 
6
- from agentx.util import get_headers
6
+ from agentx.util import get_headers, api_base
7
7
  from agentx.resources.agent import Agent
8
8
  from agentx.resources.workforce import Workforce
9
9
 
10
10
 
11
11
  class AgentX:
12
12
 
13
- def __init__(self, api_key: str = None):
13
+ def __init__(self, api_key: str = None, base_url: str = None):
14
14
  self.api_key = api_key or os.getenv("AGENTX_API_KEY")
15
15
  if self.api_key and not os.getenv("AGENTX_API_KEY"):
16
16
  os.environ["AGENTX_API_KEY"] = self.api_key
17
17
 
18
+ # base_url overrides AGENTX_API_BASE_URL env var (and the SDK default)
19
+ self.base_url = base_url or os.getenv("AGENTX_API_BASE_URL")
20
+ if self.base_url:
21
+ os.environ["AGENTX_API_BASE_URL"] = self.base_url
22
+
23
+ from agentx.evaluations.client import EvaluationsClient
24
+ from agentx.evaluations.runner import EvaluationsRunner
25
+ from agentx.version import VERSION
26
+ _eval_client = EvaluationsClient(
27
+ api_key=self.api_key,
28
+ sdk_version=VERSION,
29
+ base_url=self.base_url,
30
+ )
31
+ self.evaluations = EvaluationsRunner(_eval_client)
32
+
33
+ @classmethod
34
+ def from_env(cls) -> "AgentX":
35
+ """Create an AgentX client using AGENTX_API_KEY (and optionally AGENTX_API_BASE_URL) from the environment."""
36
+ return cls()
37
+
18
38
  def get_agent(self, id: str) -> Agent:
19
- url = f"https://api.agentx.so/api/v1/access/agents/{id}"
39
+ url = f"{api_base()}/access/agents/{id}"
20
40
  # Make a GET request to the AgentX API
21
41
  response = requests.get(url, headers=get_headers())
22
42
  # Check if response was successful
23
43
  if response.status_code == 200:
24
- agent_res = response.json()
25
- return Agent(
26
- id=agent_res.get("_id"),
27
- name=agent_res.get("name"),
28
- avatar=agent_res.get("avatar"),
29
- createdAt=agent_res.get("createdAt"),
30
- updatedAt=agent_res.get("updatedAt"),
31
- )
44
+ return Agent(**response.json())
32
45
  else:
33
46
  raise Exception(f"Failed to retrieve agent: {response.reason}")
34
47
 
35
48
  def list_agents(self) -> List[Agent]:
36
- url = "https://api.agentx.so/api/v1/access/agents"
49
+ url = f"{api_base()}/access/agents"
37
50
  # Make a GET request to the AgentX API
38
51
  response = requests.get(url, headers=get_headers())
39
52
  # Check if response was successful
@@ -45,7 +58,7 @@ class AgentX:
45
58
  @staticmethod
46
59
  def list_workforces() -> List["Workforce"]:
47
60
  """List all workforces/teams."""
48
- url = "https://api.agentx.so/api/v1/access/teams"
61
+ url = f"{api_base()}/access/teams"
49
62
  response = requests.get(url, headers=get_headers())
50
63
  if response.status_code == 200:
51
64
  return [Workforce(**workforce) for workforce in response.json()]
@@ -56,7 +69,7 @@ class AgentX:
56
69
 
57
70
  def get_profile(self):
58
71
  """Get the current user's profile information."""
59
- url = "https://api.agentx.so/api/v1/access/getProfile"
72
+ url = f"{api_base()}/access/getProfile"
60
73
  response = requests.get(url, headers=get_headers())
61
74
  if response.status_code == 200:
62
75
  return response.json()
@@ -0,0 +1,3 @@
1
+ from agentx.evaluations.client import EvaluationsClient
2
+
3
+ __all__ = ["EvaluationsClient"]
@@ -0,0 +1,69 @@
1
+ """Minimal ANSI terminal helpers — no external dependencies."""
2
+ from __future__ import annotations
3
+
4
+ import itertools
5
+ import sys
6
+ import threading
7
+ import time
8
+
9
+ _IS_TTY = hasattr(sys.stdout, "isatty") and sys.stdout.isatty()
10
+
11
+
12
+ def _c(code: str) -> str:
13
+ return f"\033[{code}m" if _IS_TTY else ""
14
+
15
+
16
+ RESET = _c("0")
17
+ BOLD = _c("1")
18
+ DIM = _c("2")
19
+ GREEN = _c("32")
20
+ YELLOW = _c("33")
21
+ RED = _c("31")
22
+ CYAN = _c("36")
23
+ MAGENTA = _c("35")
24
+
25
+
26
+ def green(s: str) -> str: return f"{GREEN}{s}{RESET}"
27
+ def yellow(s: str) -> str: return f"{YELLOW}{s}{RESET}"
28
+ def red(s: str) -> str: return f"{RED}{s}{RESET}"
29
+ def cyan(s: str) -> str: return f"{CYAN}{s}{RESET}"
30
+ def bold(s: str) -> str: return f"{BOLD}{s}{RESET}"
31
+ def dim(s: str) -> str: return f"{DIM}{s}{RESET}"
32
+ def magenta(s: str) -> str: return f"{MAGENTA}{s}{RESET}"
33
+
34
+
35
+ class Spinner:
36
+ """Inline spinner that overwrites the current line on a TTY."""
37
+
38
+ _FRAMES = ("⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏")
39
+
40
+ def __init__(self, message: str):
41
+ self._message = message
42
+ self._stop = threading.Event()
43
+ self._thread: threading.Thread | None = None
44
+
45
+ def __enter__(self) -> "Spinner":
46
+ if not _IS_TTY:
47
+ print(f" {self._message}...", flush=True)
48
+ return self
49
+ self._stop.clear()
50
+ self._thread = threading.Thread(target=self._spin, daemon=True)
51
+ self._thread.start()
52
+ return self
53
+
54
+ def __exit__(self, *_) -> None:
55
+ if not _IS_TTY:
56
+ return
57
+ self._stop.set()
58
+ if self._thread:
59
+ self._thread.join()
60
+ sys.stdout.write(f"\r{' ' * (len(self._message) + 12)}\r")
61
+ sys.stdout.flush()
62
+
63
+ def _spin(self) -> None:
64
+ for frame in itertools.cycle(self._FRAMES):
65
+ if self._stop.is_set():
66
+ break
67
+ sys.stdout.write(f"\r {CYAN}{frame}{RESET} {self._message} {DIM}(this may take ~60s+){RESET}")
68
+ sys.stdout.flush()
69
+ time.sleep(0.08)
@@ -0,0 +1,5 @@
1
+ from agentx.evaluations.adapters.raw import RawCallableAdapter
2
+ from agentx.evaluations.adapters.precomputed import PrecomputedAdapter
3
+ from agentx.evaluations.adapters.http_endpoint import HttpEndpointAdapter
4
+
5
+ __all__ = ["RawCallableAdapter", "PrecomputedAdapter", "HttpEndpointAdapter"]
@@ -0,0 +1,69 @@
1
+ from __future__ import annotations
2
+
3
+ import time
4
+ from typing import Any, Dict, Optional
5
+
6
+ import requests
7
+
8
+ from agentx.evaluations.models import EvaluationCase, EvaluationResult
9
+ from agentx.evaluations.results import normalize_error, normalize_result
10
+
11
+
12
+ class HttpEndpointAdapter:
13
+ """
14
+ Calls a user-hosted HTTP endpoint for each evaluation case.
15
+ The SDK (running locally) makes the request — the AgentX API never
16
+ touches the customer's endpoint.
17
+
18
+ The endpoint receives a POST with::
19
+
20
+ {"query": "...", "case_id": "...", "metadata": {...}}
21
+
22
+ It should respond with a JSON body containing at least ``output``
23
+ (or ``text``) and optionally ``trace`` and ``metadata``.
24
+
25
+ Usage::
26
+
27
+ adapter = HttpEndpointAdapter(
28
+ url="http://localhost:8080/eval",
29
+ headers={"Authorization": "Bearer my-token"},
30
+ timeout=30,
31
+ )
32
+ """
33
+
34
+ def __init__(
35
+ self,
36
+ url: str,
37
+ headers: Optional[Dict[str, str]] = None,
38
+ timeout: int = 30,
39
+ method: str = "POST",
40
+ ):
41
+ self._url = url
42
+ self._headers = headers or {}
43
+ self._timeout = timeout
44
+ self._method = method.upper()
45
+
46
+ def run(self, case: EvaluationCase) -> EvaluationResult:
47
+ payload = {
48
+ "query": case.query,
49
+ "case_id": case.case_id,
50
+ "question_index": case.question_index,
51
+ "run_number": case.run_number,
52
+ }
53
+ start = time.monotonic()
54
+ try:
55
+ resp = requests.request(
56
+ self._method,
57
+ self._url,
58
+ json=payload,
59
+ headers=self._headers,
60
+ timeout=self._timeout,
61
+ )
62
+ resp.raise_for_status()
63
+ elapsed = int((time.monotonic() - start) * 1000)
64
+ raw = resp.json()
65
+ except Exception as exc:
66
+ elapsed = int((time.monotonic() - start) * 1000)
67
+ return normalize_error(case, exc, latency_ms=elapsed)
68
+
69
+ return normalize_result(case, raw, latency_ms=elapsed)
@@ -0,0 +1,33 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Dict, List, Union
4
+
5
+ from agentx.evaluations.models import EvaluationCase, EvaluationResult
6
+ from agentx.evaluations.results import normalize_result
7
+
8
+
9
+ class PrecomputedAdapter:
10
+ """
11
+ Adapter for pre-computed outputs — useful when you already have agent
12
+ responses and just want AgentX to score them.
13
+
14
+ Accepts a list or dict keyed by case_id::
15
+
16
+ outputs = {
17
+ "case-0": "You can reset your password from account settings.",
18
+ "case-1": {"output": "Contact support@example.com", "metadata": {...}},
19
+ }
20
+ adapter = PrecomputedAdapter(outputs)
21
+ """
22
+
23
+ def __init__(self, outputs: Union[List[Any], Dict[str, Any]]):
24
+ if isinstance(outputs, list):
25
+ self._lookup: Dict[str, Any] = {str(i): v for i, v in enumerate(outputs)}
26
+ else:
27
+ self._lookup = {str(k): v for k, v in outputs.items()}
28
+
29
+ def run(self, case: EvaluationCase) -> EvaluationResult:
30
+ raw = self._lookup.get(case.case_id) or self._lookup.get(str(case.question_index))
31
+ if raw is None:
32
+ raw = ""
33
+ return normalize_result(case, raw, latency_ms=0)
@@ -0,0 +1,35 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Callable
4
+
5
+ from agentx.evaluations.models import EvaluationCase, EvaluationResult
6
+ from agentx.evaluations.results import normalize_error, normalize_result
7
+
8
+
9
+ class RawCallableAdapter:
10
+ """
11
+ Wraps any Python callable that accepts an EvaluationCase and returns
12
+ str | dict | EvaluationResult.
13
+
14
+ Usage::
15
+
16
+ def my_agent(case: EvaluationCase) -> str:
17
+ return my_llm.invoke(case.query)
18
+
19
+ adapter = RawCallableAdapter(my_agent)
20
+ result = adapter.run(case)
21
+ """
22
+
23
+ def __init__(self, fn: Callable[[EvaluationCase], Any]):
24
+ self._fn = fn
25
+
26
+ def run(self, case: EvaluationCase, latency_ms: int | None = None) -> EvaluationResult:
27
+ import time
28
+ start = time.monotonic()
29
+ try:
30
+ raw = self._fn(case)
31
+ except Exception as exc:
32
+ elapsed = int((time.monotonic() - start) * 1000)
33
+ return normalize_error(case, exc, latency_ms=elapsed)
34
+ elapsed = int((time.monotonic() - start) * 1000)
35
+ return normalize_result(case, raw, latency_ms=elapsed)