toolproxy 0.2.0__tar.gz → 0.4.0__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.
- {toolproxy-0.2.0 → toolproxy-0.4.0}/PKG-INFO +17 -1
- toolproxy-0.4.0/examples/caching_demo.py +96 -0
- toolproxy-0.4.0/examples/fastapi_agent.py +139 -0
- toolproxy-0.4.0/examples/persistent_memory.py +91 -0
- {toolproxy-0.2.0 → toolproxy-0.4.0}/pyproject.toml +27 -1
- {toolproxy-0.2.0 → toolproxy-0.4.0}/src/toolproxy/__init__.py +81 -2
- toolproxy-0.4.0/src/toolproxy/adapters/__init__.py +27 -0
- toolproxy-0.4.0/src/toolproxy/adapters/anthropic.py +242 -0
- toolproxy-0.4.0/src/toolproxy/adapters/azure.py +213 -0
- toolproxy-0.4.0/src/toolproxy/adapters/cohere.py +83 -0
- toolproxy-0.4.0/src/toolproxy/adapters/gemini.py +89 -0
- toolproxy-0.4.0/src/toolproxy/adapters/groq.py +83 -0
- toolproxy-0.4.0/src/toolproxy/agent.py +462 -0
- toolproxy-0.4.0/src/toolproxy/cache.py +285 -0
- {toolproxy-0.2.0 → toolproxy-0.4.0}/src/toolproxy/executor.py +40 -2
- toolproxy-0.4.0/src/toolproxy/integrations/__init__.py +1 -0
- toolproxy-0.4.0/src/toolproxy/integrations/fastapi.py +267 -0
- {toolproxy-0.2.0 → toolproxy-0.4.0}/src/toolproxy/llm_client.py +42 -5
- {toolproxy-0.2.0 → toolproxy-0.4.0}/src/toolproxy/loop.py +113 -42
- toolproxy-0.4.0/src/toolproxy/mcp.py +344 -0
- toolproxy-0.4.0/src/toolproxy/memory.py +328 -0
- toolproxy-0.4.0/src/toolproxy/observability/__init__.py +24 -0
- toolproxy-0.4.0/src/toolproxy/observability/base.py +53 -0
- toolproxy-0.4.0/src/toolproxy/observability/file.py +146 -0
- toolproxy-0.4.0/src/toolproxy/observability/otel.py +116 -0
- toolproxy-0.4.0/src/toolproxy/observability/print_tracer.py +121 -0
- toolproxy-0.4.0/src/toolproxy/retry.py +215 -0
- toolproxy-0.4.0/tests/test_adapters.py +335 -0
- toolproxy-0.4.0/tests/test_agent_extensions.py +309 -0
- toolproxy-0.4.0/tests/test_cache.py +312 -0
- toolproxy-0.4.0/tests/test_fastapi.py +237 -0
- toolproxy-0.4.0/tests/test_file_memory.py +213 -0
- toolproxy-0.4.0/tests/test_mcp.py +388 -0
- toolproxy-0.4.0/tests/test_memory.py +251 -0
- toolproxy-0.4.0/tests/test_observability.py +284 -0
- toolproxy-0.4.0/tests/test_retry.py +211 -0
- toolproxy-0.2.0/src/toolproxy/agent.py +0 -272
- {toolproxy-0.2.0 → toolproxy-0.4.0}/.gitignore +0 -0
- {toolproxy-0.2.0 → toolproxy-0.4.0}/LICENSE +0 -0
- {toolproxy-0.2.0 → toolproxy-0.4.0}/README.md +0 -0
- {toolproxy-0.2.0 → toolproxy-0.4.0}/examples/basic_chat.py +0 -0
- {toolproxy-0.2.0 → toolproxy-0.4.0}/examples/local_ollama.py +0 -0
- {toolproxy-0.2.0 → toolproxy-0.4.0}/examples/openrouter_tools.py +0 -0
- {toolproxy-0.2.0 → toolproxy-0.4.0}/src/toolproxy/config.py +0 -0
- {toolproxy-0.2.0 → toolproxy-0.4.0}/src/toolproxy/exceptions.py +0 -0
- {toolproxy-0.2.0 → toolproxy-0.4.0}/src/toolproxy/planner.py +0 -0
- {toolproxy-0.2.0 → toolproxy-0.4.0}/src/toolproxy/py.typed +0 -0
- {toolproxy-0.2.0 → toolproxy-0.4.0}/src/toolproxy/schemas.py +0 -0
- {toolproxy-0.2.0 → toolproxy-0.4.0}/src/toolproxy/tools.py +0 -0
- {toolproxy-0.2.0 → toolproxy-0.4.0}/tests/conftest.py +0 -0
- {toolproxy-0.2.0 → toolproxy-0.4.0}/tests/test_agent_basic.py +0 -0
- {toolproxy-0.2.0 → toolproxy-0.4.0}/tests/test_async_agent.py +0 -0
- {toolproxy-0.2.0 → toolproxy-0.4.0}/tests/test_emulated_mode.py +0 -0
- {toolproxy-0.2.0 → toolproxy-0.4.0}/tests/test_error_handling.py +0 -0
- {toolproxy-0.2.0 → toolproxy-0.4.0}/tests/test_native_mode.py +0 -0
- {toolproxy-0.2.0 → toolproxy-0.4.0}/tests/test_tool_registry.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: toolproxy
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Universal tool-calling wrapper for non-tool-native LLMs — emulates function calling via structured JSON planning
|
|
5
5
|
Project-URL: Homepage, https://github.com/yourusername/toolproxy
|
|
6
6
|
Project-URL: Repository, https://github.com/yourusername/toolproxy
|
|
@@ -24,12 +24,28 @@ Requires-Python: >=3.10
|
|
|
24
24
|
Requires-Dist: httpx>=0.25
|
|
25
25
|
Requires-Dist: openai>=1.0
|
|
26
26
|
Requires-Dist: pydantic>=2.0
|
|
27
|
+
Provides-Extra: anthropic
|
|
28
|
+
Requires-Dist: anthropic>=0.25; extra == 'anthropic'
|
|
29
|
+
Provides-Extra: cohere
|
|
27
30
|
Provides-Extra: dev
|
|
28
31
|
Requires-Dist: build>=1.0; extra == 'dev'
|
|
29
32
|
Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
|
|
30
33
|
Requires-Dist: pytest-mock>=3.0; extra == 'dev'
|
|
31
34
|
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
32
35
|
Requires-Dist: twine>=5.0; extra == 'dev'
|
|
36
|
+
Provides-Extra: fastapi
|
|
37
|
+
Requires-Dist: fastapi>=0.110; extra == 'fastapi'
|
|
38
|
+
Requires-Dist: httpx>=0.25; extra == 'fastapi'
|
|
39
|
+
Provides-Extra: full
|
|
40
|
+
Requires-Dist: anthropic>=0.25; extra == 'full'
|
|
41
|
+
Requires-Dist: fastapi>=0.110; extra == 'full'
|
|
42
|
+
Requires-Dist: opentelemetry-api>=1.20; extra == 'full'
|
|
43
|
+
Requires-Dist: opentelemetry-sdk>=1.20; extra == 'full'
|
|
44
|
+
Provides-Extra: gemini
|
|
45
|
+
Provides-Extra: groq
|
|
46
|
+
Provides-Extra: otel
|
|
47
|
+
Requires-Dist: opentelemetry-api>=1.20; extra == 'otel'
|
|
48
|
+
Requires-Dist: opentelemetry-sdk>=1.20; extra == 'otel'
|
|
33
49
|
Description-Content-Type: text/markdown
|
|
34
50
|
|
|
35
51
|
# toolproxy
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Caching Demo — toolproxy v0.4
|
|
3
|
+
|
|
4
|
+
Shows how InMemoryToolCache prevents repeated tool invocations when
|
|
5
|
+
the same query is made multiple times during an agent conversation.
|
|
6
|
+
|
|
7
|
+
Run with:
|
|
8
|
+
python examples/caching_demo.py
|
|
9
|
+
|
|
10
|
+
No API key needed (uses MockClient).
|
|
11
|
+
"""
|
|
12
|
+
import time
|
|
13
|
+
|
|
14
|
+
from toolproxy import UniversalAgent, tool
|
|
15
|
+
from toolproxy.cache import InMemoryToolCache
|
|
16
|
+
from toolproxy.llm_client import MockClient, ModelResponse
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# ---------------------------------------------------------------------------
|
|
20
|
+
# Simulated "expensive" tool
|
|
21
|
+
# ---------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
call_count = 0
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@tool
|
|
27
|
+
def search_web(query: str) -> str:
|
|
28
|
+
"""Search the web for information about a topic."""
|
|
29
|
+
global call_count
|
|
30
|
+
call_count += 1
|
|
31
|
+
# Simulate a slow API call
|
|
32
|
+
time.sleep(0.05)
|
|
33
|
+
print(f" [Tool call #{call_count}] search_web(query={query!r})")
|
|
34
|
+
return f"Results for '{query}': Python was created by Guido van Rossum in 1991."
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# ---------------------------------------------------------------------------
|
|
38
|
+
# Mock responses that ask for the same tool twice
|
|
39
|
+
# ---------------------------------------------------------------------------
|
|
40
|
+
|
|
41
|
+
responses = [
|
|
42
|
+
# First run: call search_web
|
|
43
|
+
ModelResponse(
|
|
44
|
+
raw_text='{"type": "tool_call", "tool": {"tool_name": "search_web", "arguments": {"query": "Python history"}}}'
|
|
45
|
+
),
|
|
46
|
+
ModelResponse(
|
|
47
|
+
raw_text='{"type": "final", "content": "Python was created in 1991."}'
|
|
48
|
+
),
|
|
49
|
+
# Second run: ask for same query again (should hit cache)
|
|
50
|
+
ModelResponse(
|
|
51
|
+
raw_text='{"type": "tool_call", "tool": {"tool_name": "search_web", "arguments": {"query": "Python history"}}}'
|
|
52
|
+
),
|
|
53
|
+
ModelResponse(
|
|
54
|
+
raw_text='{"type": "final", "content": "As I mentioned, Python was created in 1991."}'
|
|
55
|
+
),
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def main():
|
|
60
|
+
cache = InMemoryToolCache(ttl=300) # 5-minute TTL
|
|
61
|
+
|
|
62
|
+
agent = UniversalAgent(
|
|
63
|
+
model="mock/test",
|
|
64
|
+
client=MockClient(responses=responses),
|
|
65
|
+
tools=[search_web],
|
|
66
|
+
cache=cache,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
print("=" * 60)
|
|
70
|
+
print("toolproxy v0.4 — Tool Result Caching Demo")
|
|
71
|
+
print("=" * 60)
|
|
72
|
+
print()
|
|
73
|
+
|
|
74
|
+
print("Run 1: First query (cache miss — tool will be called)")
|
|
75
|
+
t0 = time.perf_counter()
|
|
76
|
+
result = agent.run("Tell me about Python's history")
|
|
77
|
+
t1 = time.perf_counter()
|
|
78
|
+
print(f" Answer: {result.content}")
|
|
79
|
+
print(f" Time: {(t1 - t0)*1000:.1f}ms")
|
|
80
|
+
print()
|
|
81
|
+
|
|
82
|
+
print("Run 2: Same query (cache HIT — tool skipped)")
|
|
83
|
+
t0 = time.perf_counter()
|
|
84
|
+
result = agent.run("Tell me about Python's history again")
|
|
85
|
+
t1 = time.perf_counter()
|
|
86
|
+
print(f" Answer: {result.content}")
|
|
87
|
+
print(f" Time: {(t1 - t0)*1000:.1f}ms")
|
|
88
|
+
print()
|
|
89
|
+
|
|
90
|
+
stats = cache.stats()
|
|
91
|
+
print(f"Cache stats: {stats}")
|
|
92
|
+
print(f"Total tool invocations: {call_count} (expected 1 — second was cached)")
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
if __name__ == "__main__":
|
|
96
|
+
main()
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"""
|
|
2
|
+
FastAPI Agent Server — toolproxy v0.4
|
|
3
|
+
|
|
4
|
+
Shows how to expose a UniversalAgent as an HTTP API using
|
|
5
|
+
the built-in FastAPI integration.
|
|
6
|
+
|
|
7
|
+
Install dependencies:
|
|
8
|
+
pip install toolproxy[fastapi] uvicorn
|
|
9
|
+
|
|
10
|
+
Run:
|
|
11
|
+
python examples/fastapi_agent.py
|
|
12
|
+
|
|
13
|
+
Then test with curl or a browser:
|
|
14
|
+
|
|
15
|
+
# Single-shot run
|
|
16
|
+
curl -X POST http://localhost:8000/agent/run \\
|
|
17
|
+
-H "Content-Type: application/json" \\
|
|
18
|
+
-d '{"prompt": "What is the weather in Chennai?"}'
|
|
19
|
+
|
|
20
|
+
# List tools
|
|
21
|
+
curl http://localhost:8000/agent/tools
|
|
22
|
+
|
|
23
|
+
# Agent info
|
|
24
|
+
curl http://localhost:8000/agent/info
|
|
25
|
+
|
|
26
|
+
# Stream (SSE)
|
|
27
|
+
curl -N -X POST http://localhost:8000/agent/stream \\
|
|
28
|
+
-H "Content-Type: application/json" \\
|
|
29
|
+
-d '{"prompt": "Tell me something interesting"}'
|
|
30
|
+
|
|
31
|
+
# Reset memory
|
|
32
|
+
curl -X POST http://localhost:8000/agent/reset
|
|
33
|
+
"""
|
|
34
|
+
from __future__ import annotations
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
import uvicorn
|
|
38
|
+
from fastapi import FastAPI
|
|
39
|
+
except ImportError:
|
|
40
|
+
print("Please install FastAPI: pip install toolproxy[fastapi] uvicorn")
|
|
41
|
+
raise SystemExit(1)
|
|
42
|
+
|
|
43
|
+
from toolproxy import UniversalAgent, tool
|
|
44
|
+
from toolproxy.cache import InMemoryToolCache
|
|
45
|
+
from toolproxy.integrations.fastapi import create_agent_router
|
|
46
|
+
from toolproxy.llm_client import MockClient, ModelResponse
|
|
47
|
+
from toolproxy.memory import ConversationBuffer
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# ---------------------------------------------------------------------------
|
|
51
|
+
# Tools
|
|
52
|
+
# ---------------------------------------------------------------------------
|
|
53
|
+
|
|
54
|
+
@tool
|
|
55
|
+
def get_weather(city: str) -> str:
|
|
56
|
+
"""Get the current weather for a city."""
|
|
57
|
+
weather_data = {
|
|
58
|
+
"chennai": "Sunny, 34°C",
|
|
59
|
+
"london": "Cloudy, 15°C",
|
|
60
|
+
"new york": "Partly cloudy, 22°C",
|
|
61
|
+
}
|
|
62
|
+
return weather_data.get(city.lower(), f"Weather data unavailable for {city}")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@tool
|
|
66
|
+
def calculate(expression: str) -> str:
|
|
67
|
+
"""Evaluate a simple mathematical expression safely."""
|
|
68
|
+
try:
|
|
69
|
+
# Only allow safe mathematical operations
|
|
70
|
+
allowed = set("0123456789+-*/()., ")
|
|
71
|
+
if not all(c in allowed for c in expression):
|
|
72
|
+
return "Error: Only basic arithmetic is supported."
|
|
73
|
+
result = eval(expression, {"__builtins__": {}}) # noqa: S307
|
|
74
|
+
return str(result)
|
|
75
|
+
except Exception as exc:
|
|
76
|
+
return f"Calculation error: {exc}"
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
# ---------------------------------------------------------------------------
|
|
80
|
+
# Mock LLM for demo (replace with a real model string for production)
|
|
81
|
+
# ---------------------------------------------------------------------------
|
|
82
|
+
|
|
83
|
+
def _make_mock_client():
|
|
84
|
+
"""Returns a MockClient with demo responses."""
|
|
85
|
+
return MockClient(
|
|
86
|
+
responses=[
|
|
87
|
+
ModelResponse(
|
|
88
|
+
raw_text='{"type": "tool_call", "tool": {"tool_name": "get_weather", "arguments": {"city": "Chennai"}}}'
|
|
89
|
+
),
|
|
90
|
+
ModelResponse(
|
|
91
|
+
raw_text='{"type": "final", "content": "The weather in Chennai is Sunny, 34°C."}'
|
|
92
|
+
),
|
|
93
|
+
]
|
|
94
|
+
* 10 # repeat for multiple requests
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
# ---------------------------------------------------------------------------
|
|
99
|
+
# Build the app
|
|
100
|
+
# ---------------------------------------------------------------------------
|
|
101
|
+
|
|
102
|
+
def create_app() -> FastAPI:
|
|
103
|
+
agent = UniversalAgent(
|
|
104
|
+
# Replace with a real model for production:
|
|
105
|
+
# model="openrouter/mistralai/mistral-7b-instruct",
|
|
106
|
+
model="mock/test",
|
|
107
|
+
client=_make_mock_client(),
|
|
108
|
+
tools=[get_weather, calculate],
|
|
109
|
+
memory=ConversationBuffer(),
|
|
110
|
+
cache=InMemoryToolCache(ttl=300),
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
app = FastAPI(
|
|
114
|
+
title="toolproxy Agent API",
|
|
115
|
+
description="Universal tool-calling agent exposed as an HTTP service.",
|
|
116
|
+
version="0.4.0",
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
# Mount the agent router at /agent
|
|
120
|
+
app.include_router(
|
|
121
|
+
create_agent_router(agent),
|
|
122
|
+
prefix="/agent",
|
|
123
|
+
tags=["Agent"],
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
@app.get("/", tags=["Health"])
|
|
127
|
+
async def health():
|
|
128
|
+
return {"status": "ok", "version": "0.4.0"}
|
|
129
|
+
|
|
130
|
+
return app
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
app = create_app()
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
if __name__ == "__main__":
|
|
137
|
+
print("Starting toolproxy FastAPI demo on http://localhost:8000")
|
|
138
|
+
print("API docs: http://localhost:8000/docs")
|
|
139
|
+
uvicorn.run(app, host="0.0.0.0", port=8000)
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Persistent Memory Demo — toolproxy v0.4
|
|
3
|
+
|
|
4
|
+
Shows JsonFileMemory that keeps conversation history across
|
|
5
|
+
multiple script runs (survives process restarts).
|
|
6
|
+
|
|
7
|
+
Run this script several times to see the agent remember prior context:
|
|
8
|
+
python examples/persistent_memory.py
|
|
9
|
+
|
|
10
|
+
No API key needed (uses MockClient that echoes memory-aware responses).
|
|
11
|
+
"""
|
|
12
|
+
import os
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
from toolproxy import UniversalAgent, tool
|
|
16
|
+
from toolproxy.llm_client import MockClient, ModelResponse
|
|
17
|
+
from toolproxy.memory import JsonFileMemory
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
HISTORY_FILE = Path("chat_history.json")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# ---------------------------------------------------------------------------
|
|
24
|
+
# Tool
|
|
25
|
+
# ---------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
@tool
|
|
28
|
+
def get_time() -> str:
|
|
29
|
+
"""Get the current time."""
|
|
30
|
+
from datetime import datetime
|
|
31
|
+
return datetime.now().strftime("%H:%M:%S")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# ---------------------------------------------------------------------------
|
|
35
|
+
# Mock responses
|
|
36
|
+
# ---------------------------------------------------------------------------
|
|
37
|
+
|
|
38
|
+
def _responses_for_run(run_number: int):
|
|
39
|
+
if run_number == 1:
|
|
40
|
+
return [
|
|
41
|
+
ModelResponse(raw_text='{"type": "final", "content": "Nice to meet you! I\'ll remember your name."}'),
|
|
42
|
+
]
|
|
43
|
+
elif run_number == 2:
|
|
44
|
+
return [
|
|
45
|
+
ModelResponse(raw_text='{"type": "final", "content": "Hello again! I remember you from our last conversation."}'),
|
|
46
|
+
]
|
|
47
|
+
else:
|
|
48
|
+
return [
|
|
49
|
+
ModelResponse(raw_text='{"type": "final", "content": "Welcome back! I still remember all our previous conversations."}'),
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def main():
|
|
54
|
+
memory = JsonFileMemory(HISTORY_FILE, max_messages=20)
|
|
55
|
+
history = memory.load()
|
|
56
|
+
run_number = max(1, len([m for m in history if m.role == "user"]) + 1)
|
|
57
|
+
|
|
58
|
+
print("=" * 60)
|
|
59
|
+
print("toolproxy v0.4 — Persistent Memory Demo")
|
|
60
|
+
print("=" * 60)
|
|
61
|
+
print(f"Run #{run_number} | History file: {HISTORY_FILE}")
|
|
62
|
+
print(f"Messages in memory: {len(history)}")
|
|
63
|
+
print()
|
|
64
|
+
|
|
65
|
+
agent = UniversalAgent(
|
|
66
|
+
model="mock/test",
|
|
67
|
+
client=MockClient(responses=_responses_for_run(run_number)),
|
|
68
|
+
tools=[get_time],
|
|
69
|
+
memory=memory,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
prompts = {
|
|
73
|
+
1: "Hi! My name is Alice.",
|
|
74
|
+
2: "Do you remember who I am?",
|
|
75
|
+
3: "How many times have we spoken?",
|
|
76
|
+
}
|
|
77
|
+
prompt = prompts.get(run_number, "Tell me something you remember.")
|
|
78
|
+
|
|
79
|
+
print(f"User: {prompt}")
|
|
80
|
+
result = agent.run(prompt)
|
|
81
|
+
print(f"Agent: {result.content}")
|
|
82
|
+
print()
|
|
83
|
+
|
|
84
|
+
updated = memory.load()
|
|
85
|
+
print(f"History now has {len(updated)} message(s).")
|
|
86
|
+
print(f"Run this script again to continue the conversation!")
|
|
87
|
+
print(f"(Delete '{HISTORY_FILE}' to start fresh)")
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
if __name__ == "__main__":
|
|
91
|
+
main()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "toolproxy"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.4.0"
|
|
4
4
|
description = "Universal tool-calling wrapper for non-tool-native LLMs — emulates function calling via structured JSON planning"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
@@ -46,6 +46,32 @@ dev = [
|
|
|
46
46
|
"build>=1.0",
|
|
47
47
|
"twine>=5.0",
|
|
48
48
|
]
|
|
49
|
+
anthropic = [
|
|
50
|
+
"anthropic>=0.25",
|
|
51
|
+
]
|
|
52
|
+
gemini = [
|
|
53
|
+
# No extra package needed — uses Google's OpenAI-compat endpoint
|
|
54
|
+
]
|
|
55
|
+
groq = [
|
|
56
|
+
# No extra package needed — uses Groq's OpenAI-compat endpoint
|
|
57
|
+
]
|
|
58
|
+
cohere = [
|
|
59
|
+
# No extra package needed — uses Cohere's OpenAI-compat endpoint
|
|
60
|
+
]
|
|
61
|
+
fastapi = [
|
|
62
|
+
"fastapi>=0.110",
|
|
63
|
+
"httpx>=0.25",
|
|
64
|
+
]
|
|
65
|
+
otel = [
|
|
66
|
+
"opentelemetry-sdk>=1.20",
|
|
67
|
+
"opentelemetry-api>=1.20",
|
|
68
|
+
]
|
|
69
|
+
full = [
|
|
70
|
+
"anthropic>=0.25",
|
|
71
|
+
"fastapi>=0.110",
|
|
72
|
+
"opentelemetry-sdk>=1.20",
|
|
73
|
+
"opentelemetry-api>=1.20",
|
|
74
|
+
]
|
|
49
75
|
|
|
50
76
|
[project.urls]
|
|
51
77
|
Homepage = "https://github.com/yourusername/toolproxy"
|
|
@@ -13,6 +13,27 @@ Public API:
|
|
|
13
13
|
Action — emulated-mode structured output
|
|
14
14
|
StreamChunk — (v0.2) streaming event from agent.stream()
|
|
15
15
|
|
|
16
|
+
Memory (v0.3):
|
|
17
|
+
BaseMemory, ConversationBuffer, SlidingWindowMemory, TokenWindowMemory
|
|
18
|
+
|
|
19
|
+
Memory persistent (v0.4):
|
|
20
|
+
JsonFileMemory
|
|
21
|
+
|
|
22
|
+
Observability (v0.3):
|
|
23
|
+
BaseTracer, FileTracer, PrintTracer, OpenTelemetryTracer
|
|
24
|
+
|
|
25
|
+
Adapters (v0.3):
|
|
26
|
+
AnthropicClient, GeminiClient, AzureOpenAIClient, GroqClient, CohereClient
|
|
27
|
+
|
|
28
|
+
MCP (v0.3):
|
|
29
|
+
MCPToolset, MCPClient, MCPToolInfo
|
|
30
|
+
|
|
31
|
+
Caching (v0.4):
|
|
32
|
+
ToolCache, InMemoryToolCache, FileToolCache
|
|
33
|
+
|
|
34
|
+
Retry (v0.4):
|
|
35
|
+
RetryConfig, with_retry, awith_retry, HTTP_RETRY_CONFIG
|
|
36
|
+
|
|
16
37
|
Utilities:
|
|
17
38
|
probe_tool_support — (v0.2) async helper to detect native tool support
|
|
18
39
|
|
|
@@ -57,6 +78,33 @@ from .schemas import (
|
|
|
57
78
|
)
|
|
58
79
|
from .tools import ToolDefinition, ToolRegistry, tool
|
|
59
80
|
|
|
81
|
+
# v0.3 — Memory
|
|
82
|
+
from .memory import BaseMemory, ConversationBuffer, SlidingWindowMemory, TokenWindowMemory
|
|
83
|
+
|
|
84
|
+
# v0.3 — Observability
|
|
85
|
+
from .observability import BaseTracer, FileTracer, PrintTracer, OpenTelemetryTracer
|
|
86
|
+
|
|
87
|
+
# v0.3 — Adapters (lazy imports — no hard deps until used)
|
|
88
|
+
from .adapters import (
|
|
89
|
+
AnthropicClient,
|
|
90
|
+
GeminiClient,
|
|
91
|
+
AzureOpenAIClient,
|
|
92
|
+
GroqClient,
|
|
93
|
+
CohereClient,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
# v0.3 — MCP
|
|
97
|
+
from .mcp import MCPClient, MCPToolInfo, MCPToolset
|
|
98
|
+
|
|
99
|
+
# v0.4 — Caching
|
|
100
|
+
from .cache import FileToolCache, InMemoryToolCache, ToolCache
|
|
101
|
+
|
|
102
|
+
# v0.4 — Retry
|
|
103
|
+
from .retry import HTTP_RETRY_CONFIG, RetryConfig, awith_retry, with_retry
|
|
104
|
+
|
|
105
|
+
# v0.4 — Persistent memory (JsonFileMemory added to memory module)
|
|
106
|
+
from .memory import JsonFileMemory
|
|
107
|
+
|
|
60
108
|
__all__ = [
|
|
61
109
|
# Main API
|
|
62
110
|
"UniversalAgent",
|
|
@@ -76,7 +124,7 @@ __all__ = [
|
|
|
76
124
|
"ToolCall",
|
|
77
125
|
"ToolResult",
|
|
78
126
|
"TraceEntry",
|
|
79
|
-
#
|
|
127
|
+
# Core clients
|
|
80
128
|
"LLMClient",
|
|
81
129
|
"ModelResponse",
|
|
82
130
|
"OpenRouterClient",
|
|
@@ -84,6 +132,37 @@ __all__ = [
|
|
|
84
132
|
"OllamaClient",
|
|
85
133
|
"MockClient",
|
|
86
134
|
"get_client",
|
|
135
|
+
# v0.3 Adapters
|
|
136
|
+
"AnthropicClient",
|
|
137
|
+
"GeminiClient",
|
|
138
|
+
"AzureOpenAIClient",
|
|
139
|
+
"GroqClient",
|
|
140
|
+
"CohereClient",
|
|
141
|
+
# v0.3 Memory
|
|
142
|
+
"BaseMemory",
|
|
143
|
+
"ConversationBuffer",
|
|
144
|
+
"SlidingWindowMemory",
|
|
145
|
+
"TokenWindowMemory",
|
|
146
|
+
# v0.3 Observability
|
|
147
|
+
"BaseTracer",
|
|
148
|
+
"FileTracer",
|
|
149
|
+
"PrintTracer",
|
|
150
|
+
"OpenTelemetryTracer",
|
|
151
|
+
# v0.3 MCP
|
|
152
|
+
"MCPClient",
|
|
153
|
+
"MCPToolInfo",
|
|
154
|
+
"MCPToolset",
|
|
155
|
+
# v0.4 Caching
|
|
156
|
+
"ToolCache",
|
|
157
|
+
"InMemoryToolCache",
|
|
158
|
+
"FileToolCache",
|
|
159
|
+
# v0.4 Retry
|
|
160
|
+
"RetryConfig",
|
|
161
|
+
"with_retry",
|
|
162
|
+
"awith_retry",
|
|
163
|
+
"HTTP_RETRY_CONFIG",
|
|
164
|
+
# v0.4 Persistent Memory
|
|
165
|
+
"JsonFileMemory",
|
|
87
166
|
# Exceptions
|
|
88
167
|
"UniversalAgentError",
|
|
89
168
|
"ToolNotFoundError",
|
|
@@ -94,4 +173,4 @@ __all__ = [
|
|
|
94
173
|
"ExecutionPolicyError",
|
|
95
174
|
]
|
|
96
175
|
|
|
97
|
-
__version__ = "0.
|
|
176
|
+
__version__ = "0.4.0"
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""
|
|
2
|
+
toolproxy.adapters — provider-specific LLM client implementations.
|
|
3
|
+
|
|
4
|
+
New providers added in v0.3:
|
|
5
|
+
AnthropicClient — Claude models via the Anthropic API.
|
|
6
|
+
GeminiClient — Gemini models via Google's OpenAI-compat endpoint.
|
|
7
|
+
AzureOpenAIClient — GPT models via Azure OpenAI Service.
|
|
8
|
+
GroqClient — Fast inference via Groq's OpenAI-compat API.
|
|
9
|
+
CohereClient — Command R+ via Cohere's OpenAI-compat v2 API.
|
|
10
|
+
|
|
11
|
+
Each adapter uses optional imports: the underlying SDK or httpx is imported
|
|
12
|
+
only when the client is instantiated, giving a clear error message if the
|
|
13
|
+
required package is not installed.
|
|
14
|
+
"""
|
|
15
|
+
from .anthropic import AnthropicClient
|
|
16
|
+
from .gemini import GeminiClient
|
|
17
|
+
from .azure import AzureOpenAIClient
|
|
18
|
+
from .groq import GroqClient
|
|
19
|
+
from .cohere import CohereClient
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"AnthropicClient",
|
|
23
|
+
"GeminiClient",
|
|
24
|
+
"AzureOpenAIClient",
|
|
25
|
+
"GroqClient",
|
|
26
|
+
"CohereClient",
|
|
27
|
+
]
|