agentx-python 0.8.28__tar.gz → 0.8.29__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.
- {agentx_python-0.8.28 → agentx_python-0.8.29}/PKG-INFO +1 -1
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/evaluations/client.py +43 -1
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/evaluations/tool_schemas.py +29 -5
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/monitor/client.py +10 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/version.py +2 -2
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx_python.egg-info/PKG-INFO +1 -1
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx_python.egg-info/SOURCES.txt +1 -0
- agentx_python-0.8.29/tests/test_tool_schemas.py +81 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/LICENSE +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/README.md +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/__init__.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/agentx.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/cli.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/evaluations/__init__.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/evaluations/_term.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/evaluations/adapters/__init__.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/evaluations/adapters/http_endpoint.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/evaluations/adapters/precomputed.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/evaluations/adapters/raw.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/evaluations/datasets.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/evaluations/evaluation_settings.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/evaluations/models.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/evaluations/prompts.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/evaluations/reporting.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/evaluations/results.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/evaluations/runner.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/evaluations/tracing.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/exceptions.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/export.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/feedback.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/integrations/__init__.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/integrations/_traced_call.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/integrations/anthropic.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/integrations/autogen.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/integrations/crewai.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/integrations/databricks.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/integrations/google_adk.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/integrations/google_genai.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/integrations/langchain.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/integrations/litellm.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/integrations/llamaindex.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/integrations/moveworks.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/integrations/nvidia_nim.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/integrations/openai.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/integrations/openai_agents.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/monitor/__init__.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/monitor/_transport.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/monitor/agents.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/monitor/alert_rules.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/monitor/improvement_groups.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/monitor/judge_scorers.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/monitor/models.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/monitor/online_evaluators.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/monitor/patterns.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/monitor/profile.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/monitor/review_queue.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/monitor/rules.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/monitor/scorer_groups.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/monitor/scorers.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/monitor/sessions.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/monitor/signals.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/outcomes.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/projects.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/py.typed +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/resources/__init__.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/resources/agent.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/resources/conversation.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/resources/workforce.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/testing.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/traces.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/tracing/__init__.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/tracing/ci_types.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/tracing/eval_scope.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/tracing/framework_detect.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/tracing/ingest_client.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/tracing/tracer.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx/util.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx_python.egg-info/dependency_links.txt +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx_python.egg-info/entry_points.txt +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx_python.egg-info/not-zip-safe +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx_python.egg-info/requires.txt +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/agentx_python.egg-info/top_level.txt +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/setup.cfg +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/setup.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/tests/test_alert_rules.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/tests/test_cli_launcher.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/tests/test_deep_dive_fixes.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/tests/test_docs_match_sdk.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/tests/test_error_taxonomy.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/tests/test_eval_scope.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/tests/test_framework_detect.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/tests/test_integration.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/tests/test_integrations.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/tests/test_judge_scorers.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/tests/test_multi_judge.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/tests/test_pairwise.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/tests/test_pattern_update_merge.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/tests/test_review_queue.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/tests/test_runner_features.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/tests/test_selfhost_analysis_fallback.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/tests/test_selfhost_compat.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/tests/test_span_tree.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/tests/test_testing.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/tests/test_wire_models.py +0 -0
- {agentx_python-0.8.28 → agentx_python-0.8.29}/tests/test_workforce_binding.py +0 -0
|
@@ -75,6 +75,22 @@ def _resolve_scorer_id(scorer_id: Optional[str], evaluation_settings_id: Optiona
|
|
|
75
75
|
return scorer_id or evaluation_settings_id
|
|
76
76
|
|
|
77
77
|
|
|
78
|
+
# "Argument not given" for update_tool_schema_meta, where None is itself a meaningful value
|
|
79
|
+
# (clear the price).
|
|
80
|
+
_UNSET: object = object()
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _validated_tool_price(value: float) -> float:
|
|
84
|
+
"""Local mirror of the engine's check: USD per call, finite and non-negative."""
|
|
85
|
+
try:
|
|
86
|
+
price = float(value)
|
|
87
|
+
except (TypeError, ValueError):
|
|
88
|
+
raise ValueError(f"price_per_call_usd must be a number of USD per call, got {value!r}") from None
|
|
89
|
+
if price != price or price in (float("inf"), float("-inf")) or price < 0:
|
|
90
|
+
raise ValueError(f"price_per_call_usd must be a finite, non-negative number, got {value!r}")
|
|
91
|
+
return price
|
|
92
|
+
|
|
93
|
+
|
|
78
94
|
class EvaluationsClient:
|
|
79
95
|
def __init__(
|
|
80
96
|
self,
|
|
@@ -723,13 +739,39 @@ class EvaluationsClient:
|
|
|
723
739
|
data = self._request("GET", "/evaluate/tool-schemas", base=self._api_root)
|
|
724
740
|
return data.get("toolSchemas", []) if isinstance(data, dict) else data
|
|
725
741
|
|
|
726
|
-
def create_tool_schema(
|
|
742
|
+
def create_tool_schema(
|
|
743
|
+
self, *, name: str, definition: str, description: Optional[str] = None,
|
|
744
|
+
price_per_call_usd: Optional[float] = None,
|
|
745
|
+
) -> dict:
|
|
727
746
|
payload: dict = {"name": name, "definition": definition}
|
|
728
747
|
if description is not None:
|
|
729
748
|
payload["description"] = description
|
|
749
|
+
if price_per_call_usd is not None:
|
|
750
|
+
payload["pricePerCallUsd"] = _validated_tool_price(price_per_call_usd)
|
|
730
751
|
# Server-side write - no transport retry (see init_run's comment).
|
|
731
752
|
return self._request("POST", "/evaluate/tool-schemas", base=self._api_root, json=payload, retry=False)
|
|
732
753
|
|
|
754
|
+
def update_tool_schema_meta(
|
|
755
|
+
self, tool_schema_id: str, *, description: Optional[str] = None,
|
|
756
|
+
price_per_call_usd: Optional[float] = _UNSET, test_endpoint_url: Optional[str] = _UNSET, # type: ignore[assignment]
|
|
757
|
+
) -> dict:
|
|
758
|
+
"""Metadata-only edit (PATCH): description, per-call price, test endpoint. Never touches
|
|
759
|
+
the version log. ``price_per_call_usd=None`` clears the price (the tool becomes free)."""
|
|
760
|
+
payload: dict = {}
|
|
761
|
+
if description is not None:
|
|
762
|
+
payload["description"] = description
|
|
763
|
+
if price_per_call_usd is not _UNSET:
|
|
764
|
+
payload["pricePerCallUsd"] = None if price_per_call_usd is None else _validated_tool_price(price_per_call_usd)
|
|
765
|
+
if test_endpoint_url is not _UNSET:
|
|
766
|
+
payload["testEndpointUrl"] = test_endpoint_url
|
|
767
|
+
return self._request(
|
|
768
|
+
"PATCH", f"/evaluate/tool-schemas/{tool_schema_id}", base=self._api_root, json=payload, retry=False,
|
|
769
|
+
)
|
|
770
|
+
|
|
771
|
+
def delete_tool_schema(self, tool_schema_id: str) -> None:
|
|
772
|
+
"""Deletes the tool schema and its version history."""
|
|
773
|
+
self._request("DELETE", f"/evaluate/tool-schemas/{tool_schema_id}", base=self._api_root, retry=False)
|
|
774
|
+
|
|
733
775
|
def get_tool_schema_examples(self, tool_schema_id: str, window: Optional[str] = None) -> dict:
|
|
734
776
|
params = {"window": window} if window else None
|
|
735
777
|
return self._request(
|
|
@@ -19,15 +19,39 @@ class ToolSchemaClient:
|
|
|
19
19
|
def list(self) -> List[dict]:
|
|
20
20
|
return self._client.list_tool_schemas()
|
|
21
21
|
|
|
22
|
-
def create(
|
|
23
|
-
|
|
22
|
+
def create(
|
|
23
|
+
self, *, name: str, definition: str, description: Optional[str] = None,
|
|
24
|
+
price_per_call_usd: Optional[float] = None,
|
|
25
|
+
) -> dict:
|
|
26
|
+
"""``price_per_call_usd`` is what one call of this tool costs you (a paid external API
|
|
27
|
+
billed per request). Every recorded call is charged, failed ones included; the spend
|
|
28
|
+
shows as the "Tool calls" segment of the Overview cost chart and counts toward the
|
|
29
|
+
``estimatedCostUsd`` alert metric. Leave it unset for a free tool."""
|
|
30
|
+
return self._client.create_tool_schema(
|
|
31
|
+
name=name, definition=definition, description=description, price_per_call_usd=price_per_call_usd,
|
|
32
|
+
)
|
|
24
33
|
|
|
25
|
-
def get_or_create(
|
|
26
|
-
|
|
34
|
+
def get_or_create(
|
|
35
|
+
self, *, name: str, definition: str, description: Optional[str] = None,
|
|
36
|
+
price_per_call_usd: Optional[float] = None,
|
|
37
|
+
) -> dict:
|
|
38
|
+
"""Idempotent register: returns the existing schema of this name if present (its stored
|
|
39
|
+
price wins - use :meth:`set_price` to change it)."""
|
|
27
40
|
existing = next((t for t in self.list() if t.get("name") == name), None)
|
|
28
41
|
if existing:
|
|
29
42
|
return existing
|
|
30
|
-
return self.create(
|
|
43
|
+
return self.create(
|
|
44
|
+
name=name, definition=definition, description=description, price_per_call_usd=price_per_call_usd,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
def set_price(self, tool_schema_id: str, price_per_call_usd: Optional[float]) -> dict:
|
|
48
|
+
"""Set (or clear with ``None``) a registered tool's USD price per call. Metadata only -
|
|
49
|
+
the definition's version log is untouched."""
|
|
50
|
+
return self._client.update_tool_schema_meta(tool_schema_id, price_per_call_usd=price_per_call_usd)
|
|
51
|
+
|
|
52
|
+
def delete(self, tool_schema_id: str) -> None:
|
|
53
|
+
"""Deletes the tool schema and its version history."""
|
|
54
|
+
self._client.delete_tool_schema(tool_schema_id)
|
|
31
55
|
|
|
32
56
|
def examples(self, tool_schema_id: str, window: Optional[str] = None) -> dict:
|
|
33
57
|
"""Failure evidence recorded against this tool (agent-tool-failure signals, low-rated
|
|
@@ -360,6 +360,16 @@ class MonitorClient:
|
|
|
360
360
|
params[key] = value
|
|
361
361
|
return self._request("GET", "/metrics", params=params)
|
|
362
362
|
|
|
363
|
+
def cost_trend(self, window: str = "7d") -> dict:
|
|
364
|
+
"""Overview's "Total cost" chart over a window ("24h", "7d", "30d"): priced spend per
|
|
365
|
+
bucket and in total, stacked by model, with two reserved segments - ``"eval runs"``
|
|
366
|
+
(offline-evaluation spend) and ``"tool calls"`` (registered tools' per-call prices, split
|
|
367
|
+
per tool in ``totalsByTool``). Unpriced models and tools contribute $0."""
|
|
368
|
+
return self._request(
|
|
369
|
+
"GET", "/agent-monitoring/cost-trend",
|
|
370
|
+
base=self._api_root(), params={"window": window},
|
|
371
|
+
)
|
|
372
|
+
|
|
363
373
|
def topics(self, window: str = "7d") -> dict:
|
|
364
374
|
"""The Topics view's data over a window ("24h", "7d", "30d"): LLM-classified themes of
|
|
365
375
|
sampled production traffic with per-topic counts and sentiment. Empty until Topics is
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
VERSION = "0.8.
|
|
1
|
+
VERSION = "0.8.29"
|
|
2
2
|
|
|
3
3
|
# The AgentX-trace-eval release this SDK version is tested against - what `agentx-trace-eval`
|
|
4
4
|
# installs and converges to (see agentx/cli.py). Bump together with VERSION when releasing, so
|
|
5
5
|
# every published SDK names a known-good engine+dashboard pair. Users can override with
|
|
6
6
|
# AGENTX_TRACE_EVAL_VERSION=<tag|latest>.
|
|
7
|
-
ENGINE_VERSION = "v0.3.
|
|
7
|
+
ENGINE_VERSION = "v0.3.32"
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""Unit tests for the tool schema registry client - wire-level, no engine required: the
|
|
2
|
+
per-call price rides the camelCase wire on create, PATCH edits are metadata-only and can clear
|
|
3
|
+
the price with None, and a bad price is refused locally before any request."""
|
|
4
|
+
|
|
5
|
+
from typing import Any, Dict, List
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
from agentx.evaluations.client import EvaluationsClient
|
|
10
|
+
from agentx.evaluations.tool_schemas import ToolSchemaClient
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class RecordingClient(EvaluationsClient):
|
|
14
|
+
def __init__(self, responses: List[Any]):
|
|
15
|
+
super().__init__(api_key="agtx_test", base_url="http://engine:4700/api/v1")
|
|
16
|
+
self.calls: List[Dict[str, Any]] = []
|
|
17
|
+
self._responses = responses
|
|
18
|
+
|
|
19
|
+
def _request(self, method: str, path: str, timeout: int = 30, base=None, retry: bool = True, **kwargs: Any) -> Any: # type: ignore[override]
|
|
20
|
+
self.calls.append({"method": method, "path": path, "retry": retry, **kwargs})
|
|
21
|
+
return self._responses.pop(0) if self._responses else {}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_create_sends_the_price_on_the_camelcase_wire_without_retry():
|
|
25
|
+
client = RecordingClient([{"_id": "t1", "name": "geocode", "pricePerCallUsd": 0.25}])
|
|
26
|
+
created = ToolSchemaClient(client).create(name="geocode", definition="{}", price_per_call_usd=0.25)
|
|
27
|
+
call = client.calls[0]
|
|
28
|
+
assert call["method"] == "POST"
|
|
29
|
+
assert call["path"] == "/evaluate/tool-schemas"
|
|
30
|
+
assert call["retry"] is False
|
|
31
|
+
assert call["json"] == {"name": "geocode", "definition": "{}", "pricePerCallUsd": 0.25}
|
|
32
|
+
assert created["pricePerCallUsd"] == 0.25
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def test_create_omits_the_price_when_not_given():
|
|
36
|
+
client = RecordingClient([{}])
|
|
37
|
+
ToolSchemaClient(client).create(name="clock", definition="{}")
|
|
38
|
+
assert "pricePerCallUsd" not in client.calls[0]["json"]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def test_set_price_patches_metadata_and_none_clears_it():
|
|
42
|
+
client = RecordingClient([{"ok": True}, {"ok": True}])
|
|
43
|
+
tools = ToolSchemaClient(client)
|
|
44
|
+
tools.set_price("t1", 0.01)
|
|
45
|
+
tools.set_price("t1", None)
|
|
46
|
+
assert [c["method"] for c in client.calls] == ["PATCH", "PATCH"]
|
|
47
|
+
assert client.calls[0]["path"] == "/evaluate/tool-schemas/t1"
|
|
48
|
+
assert client.calls[0]["json"] == {"pricePerCallUsd": 0.01}
|
|
49
|
+
assert client.calls[1]["json"] == {"pricePerCallUsd": None}
|
|
50
|
+
assert all(c["retry"] is False for c in client.calls)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_update_meta_sends_only_the_fields_given():
|
|
54
|
+
client = RecordingClient([{"ok": True}])
|
|
55
|
+
client.update_tool_schema_meta("t1", description="Order lookup")
|
|
56
|
+
assert client.calls[0]["json"] == {"description": "Order lookup"}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def test_a_bad_price_is_refused_before_any_request():
|
|
60
|
+
client = RecordingClient([])
|
|
61
|
+
tools = ToolSchemaClient(client)
|
|
62
|
+
with pytest.raises(ValueError, match="non-negative"):
|
|
63
|
+
tools.create(name="x", definition="{}", price_per_call_usd=-1)
|
|
64
|
+
with pytest.raises(ValueError, match="number"):
|
|
65
|
+
tools.set_price("t1", "cheap") # type: ignore[arg-type]
|
|
66
|
+
assert client.calls == []
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def test_delete_never_retries():
|
|
70
|
+
client = RecordingClient([{}])
|
|
71
|
+
ToolSchemaClient(client).delete("t1")
|
|
72
|
+
assert client.calls[0]["method"] == "DELETE"
|
|
73
|
+
assert client.calls[0]["path"] == "/evaluate/tool-schemas/t1"
|
|
74
|
+
assert client.calls[0]["retry"] is False
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_get_or_create_keeps_the_existing_row_and_its_price():
|
|
78
|
+
client = RecordingClient([{"toolSchemas": [{"_id": "t1", "name": "geocode", "pricePerCallUsd": 0.25}]}])
|
|
79
|
+
existing = ToolSchemaClient(client).get_or_create(name="geocode", definition="{}", price_per_call_usd=9)
|
|
80
|
+
assert existing["pricePerCallUsd"] == 0.25
|
|
81
|
+
assert [c["method"] for c in client.calls] == ["GET"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|