mbxai 0.7.2__tar.gz → 0.8.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.
- {mbxai-0.7.2 → mbxai-0.8.0}/PKG-INFO +1 -1
- {mbxai-0.7.2 → mbxai-0.8.0}/pyproject.toml +1 -1
- {mbxai-0.7.2 → mbxai-0.8.0}/setup.py +1 -1
- {mbxai-0.7.2 → mbxai-0.8.0}/src/mbxai/__init__.py +1 -1
- mbxai-0.8.0/src/mbxai/examples/openrouter_example.py +45 -0
- mbxai-0.8.0/src/mbxai/examples/parse_example.py +99 -0
- mbxai-0.8.0/src/mbxai/examples/parse_tool_example.py +144 -0
- mbxai-0.8.0/src/mbxai/examples/tool_client_example.py +126 -0
- {mbxai-0.7.2 → mbxai-0.8.0}/src/mbxai/mcp/server.py +1 -1
- {mbxai-0.7.2 → mbxai-0.8.0}/src/mbxai/openrouter/client.py +21 -92
- {mbxai-0.7.2 → mbxai-0.8.0}/src/mbxai/tools/client.py +54 -76
- mbxai-0.7.2/tests/test_core.py +0 -9
- mbxai-0.7.2/tests/test_mcp.py +0 -355
- mbxai-0.7.2/tests/test_openrouter.py +0 -485
- mbxai-0.7.2/tests/test_tools.py +0 -319
- {mbxai-0.7.2 → mbxai-0.8.0}/.gitignore +0 -0
- {mbxai-0.7.2 → mbxai-0.8.0}/LICENSE +0 -0
- {mbxai-0.7.2 → mbxai-0.8.0}/README.md +0 -0
- {mbxai-0.7.2 → mbxai-0.8.0}/src/mbxai/core.py +0 -0
- {mbxai-0.7.2 → mbxai-0.8.0}/src/mbxai/mcp/__init__.py +0 -0
- {mbxai-0.7.2 → mbxai-0.8.0}/src/mbxai/mcp/client.py +0 -0
- {mbxai-0.7.2 → mbxai-0.8.0}/src/mbxai/mcp/example.py +0 -0
- {mbxai-0.7.2 → mbxai-0.8.0}/src/mbxai/openrouter/__init__.py +0 -0
- {mbxai-0.7.2 → mbxai-0.8.0}/src/mbxai/openrouter/config.py +0 -0
- {mbxai-0.7.2 → mbxai-0.8.0}/src/mbxai/openrouter/models.py +0 -0
- {mbxai-0.7.2 → mbxai-0.8.0}/src/mbxai/tools/__init__.py +0 -0
- {mbxai-0.7.2 → mbxai-0.8.0}/src/mbxai/tools/example.py +0 -0
- {mbxai-0.7.2 → mbxai-0.8.0}/src/mbxai/tools/types.py +0 -0
@@ -0,0 +1,45 @@
|
|
1
|
+
"""
|
2
|
+
Example script demonstrating basic usage of the OpenRouterClient.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import os
|
6
|
+
import logging
|
7
|
+
from mbxai.openrouter.client import OpenRouterClient, OpenRouterModel
|
8
|
+
|
9
|
+
# Configure logging
|
10
|
+
logging.basicConfig(
|
11
|
+
level=logging.INFO,
|
12
|
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
13
|
+
)
|
14
|
+
logger = logging.getLogger(__name__)
|
15
|
+
|
16
|
+
def main():
|
17
|
+
# Get API token from environment variable
|
18
|
+
token = os.getenv("OPENROUTER_API_KEY")
|
19
|
+
if not token:
|
20
|
+
logger.error("OPENROUTER_API_KEY environment variable not set")
|
21
|
+
raise ValueError("Please set the OPENROUTER_API_KEY environment variable")
|
22
|
+
|
23
|
+
logger.info("Initializing OpenRouterClient with GPT-4 Turbo")
|
24
|
+
# Initialize the client
|
25
|
+
client = OpenRouterClient(
|
26
|
+
token=token,
|
27
|
+
model=OpenRouterModel.GPT4_TURBO # Using GPT-4 Turbo as default
|
28
|
+
)
|
29
|
+
|
30
|
+
# Example messages
|
31
|
+
messages = [
|
32
|
+
{"role": "system", "content": "You are a helpful assistant."},
|
33
|
+
{"role": "user", "content": "What is the capital of France?"}
|
34
|
+
]
|
35
|
+
|
36
|
+
logger.info("Sending request to OpenRouter API")
|
37
|
+
# Send the request
|
38
|
+
response = client.create(messages=messages)
|
39
|
+
|
40
|
+
# Log the response
|
41
|
+
logger.info("Received response from OpenRouter API")
|
42
|
+
logger.info(f"Response: {response}")
|
43
|
+
|
44
|
+
if __name__ == "__main__":
|
45
|
+
main()
|
@@ -0,0 +1,99 @@
|
|
1
|
+
"""
|
2
|
+
Example script demonstrating how to use the parse function with OpenRouterClient.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import os
|
6
|
+
import logging
|
7
|
+
from typing import Any
|
8
|
+
from pydantic import BaseModel, Field
|
9
|
+
from mbxai.openrouter.client import OpenRouterClient, OpenRouterModel
|
10
|
+
|
11
|
+
# Configure logging
|
12
|
+
logging.basicConfig(
|
13
|
+
level=logging.INFO,
|
14
|
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
15
|
+
)
|
16
|
+
logger = logging.getLogger(__name__)
|
17
|
+
|
18
|
+
# Define a Pydantic model for structured weather data
|
19
|
+
class WeatherData(BaseModel):
|
20
|
+
"""Weather data for a location."""
|
21
|
+
location: str = Field(..., description="The city name")
|
22
|
+
temperature: float = Field(..., description="Temperature in Celsius")
|
23
|
+
condition: str = Field(..., description="Weather condition (e.g., sunny, cloudy)")
|
24
|
+
humidity: float = Field(..., description="Humidity percentage")
|
25
|
+
wind_speed: float = Field(..., description="Wind speed in km/h")
|
26
|
+
feels_like: float = Field(..., description="Feels like temperature in Celsius")
|
27
|
+
precipitation_chance: float = Field(..., description="Chance of precipitation as a percentage")
|
28
|
+
|
29
|
+
async def main():
|
30
|
+
# Get API token from environment variable
|
31
|
+
token = os.getenv("OPENROUTER_API_KEY")
|
32
|
+
if not token:
|
33
|
+
raise ValueError("Please set the OPENROUTER_API_KEY environment variable")
|
34
|
+
|
35
|
+
# Initialize the OpenRouter client
|
36
|
+
logger.info("Initializing OpenRouter client")
|
37
|
+
client = OpenRouterClient(
|
38
|
+
token=token,
|
39
|
+
model=OpenRouterModel.GPT41
|
40
|
+
)
|
41
|
+
|
42
|
+
# Example 1: Parse weather data for a single location
|
43
|
+
logger.info("Parsing weather data for New York")
|
44
|
+
messages = [
|
45
|
+
{
|
46
|
+
"role": "user",
|
47
|
+
"content": "What's the current weather in New York? Please provide temperature, condition, humidity, wind speed, feels like temperature, and precipitation chance."
|
48
|
+
}
|
49
|
+
]
|
50
|
+
|
51
|
+
response = client.parse(
|
52
|
+
messages=messages,
|
53
|
+
response_format=WeatherData,
|
54
|
+
timeout=30.0,
|
55
|
+
)
|
56
|
+
|
57
|
+
weather_data = response.choices[0].message.parsed
|
58
|
+
print("\nWeather data for New York:")
|
59
|
+
print(f"Location: {weather_data.location}")
|
60
|
+
print(f"Temperature: {weather_data.temperature}°C")
|
61
|
+
print(f"Condition: {weather_data.condition}")
|
62
|
+
print(f"Humidity: {weather_data.humidity}%")
|
63
|
+
print(f"Wind Speed: {weather_data.wind_speed} km/h")
|
64
|
+
print(f"Feels Like: {weather_data.feels_like}°C")
|
65
|
+
print(f"Precipitation Chance: {weather_data.precipitation_chance}%")
|
66
|
+
|
67
|
+
# Example 2: Parse weather data for multiple locations
|
68
|
+
logger.info("\nParsing weather data for multiple locations")
|
69
|
+
messages = [
|
70
|
+
{
|
71
|
+
"role": "user",
|
72
|
+
"content": "Compare the weather in London and Tokyo. For each city, provide temperature, condition, humidity, wind speed, feels like temperature, and precipitation chance."
|
73
|
+
}
|
74
|
+
]
|
75
|
+
|
76
|
+
class MultiLocationWeather(BaseModel):
|
77
|
+
"""Weather data for multiple locations."""
|
78
|
+
locations: list[WeatherData] = Field(..., description="List of weather data for different locations")
|
79
|
+
|
80
|
+
response = client.parse(
|
81
|
+
messages=messages,
|
82
|
+
response_format=MultiLocationWeather,
|
83
|
+
timeout=30.0,
|
84
|
+
)
|
85
|
+
|
86
|
+
multi_weather = response.choices[0].message.parsed
|
87
|
+
print("\nWeather comparison:")
|
88
|
+
for location_data in multi_weather.locations:
|
89
|
+
print(f"\n{location_data.location}:")
|
90
|
+
print(f"Temperature: {location_data.temperature}°C")
|
91
|
+
print(f"Condition: {location_data.condition}")
|
92
|
+
print(f"Humidity: {location_data.humidity}%")
|
93
|
+
print(f"Wind Speed: {location_data.wind_speed} km/h")
|
94
|
+
print(f"Feels Like: {location_data.feels_like}°C")
|
95
|
+
print(f"Precipitation Chance: {location_data.precipitation_chance}%")
|
96
|
+
|
97
|
+
if __name__ == "__main__":
|
98
|
+
import asyncio
|
99
|
+
asyncio.run(main())
|
@@ -0,0 +1,144 @@
|
|
1
|
+
"""
|
2
|
+
Example script demonstrating how to use both parse and tools with OpenRouterClient.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import os
|
6
|
+
import logging
|
7
|
+
import random
|
8
|
+
from typing import Any
|
9
|
+
from pydantic import BaseModel, Field
|
10
|
+
from mbxai.openrouter.client import OpenRouterClient, OpenRouterModel
|
11
|
+
from mbxai.tools.client import ToolClient
|
12
|
+
|
13
|
+
# Configure logging
|
14
|
+
logging.basicConfig(
|
15
|
+
level=logging.INFO,
|
16
|
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
17
|
+
)
|
18
|
+
logger = logging.getLogger(__name__)
|
19
|
+
|
20
|
+
# Define a Pydantic model for structured weather data
|
21
|
+
class WeatherData(BaseModel):
|
22
|
+
"""Weather data for a location."""
|
23
|
+
location: str = Field(..., description="The city name")
|
24
|
+
temperature: float = Field(..., description="Temperature in Celsius")
|
25
|
+
condition: str = Field(..., description="Weather condition (e.g., sunny, cloudy)")
|
26
|
+
humidity: float = Field(..., description="Humidity percentage")
|
27
|
+
wind_speed: float = Field(..., description="Wind speed in km/h")
|
28
|
+
feels_like: float = Field(..., description="Feels like temperature in Celsius")
|
29
|
+
precipitation_chance: float = Field(..., description="Chance of precipitation as a percentage")
|
30
|
+
|
31
|
+
# Mock weather data for demonstration
|
32
|
+
WEATHER_DATA = {
|
33
|
+
"new york": {"temperature": 22.5, "condition": "sunny", "humidity": 65, "wind_speed": 12, "feels_like": 23.0, "precipitation_chance": 10},
|
34
|
+
"london": {"temperature": 18.2, "condition": "cloudy", "humidity": 75, "wind_speed": 8, "feels_like": 17.5, "precipitation_chance": 40},
|
35
|
+
"tokyo": {"temperature": 25.7, "condition": "clear", "humidity": 60, "wind_speed": 5, "feels_like": 26.0, "precipitation_chance": 5},
|
36
|
+
"paris": {"temperature": 20.1, "condition": "partly cloudy", "humidity": 70, "wind_speed": 10, "feels_like": 19.5, "precipitation_chance": 20},
|
37
|
+
}
|
38
|
+
|
39
|
+
def get_weather(location: str) -> dict[str, Any]:
|
40
|
+
"""Get weather information for a location.
|
41
|
+
|
42
|
+
Args:
|
43
|
+
location: The city name to get weather for
|
44
|
+
|
45
|
+
Returns:
|
46
|
+
Weather information including temperature, condition, humidity, and wind speed
|
47
|
+
"""
|
48
|
+
logger.info(f"Getting weather for location: {location}")
|
49
|
+
|
50
|
+
# Convert location to lowercase for case-insensitive matching
|
51
|
+
location = location.lower()
|
52
|
+
|
53
|
+
# Get weather data or generate random data for unknown locations
|
54
|
+
if location in WEATHER_DATA:
|
55
|
+
weather = WEATHER_DATA[location]
|
56
|
+
else:
|
57
|
+
logger.warning(f"No weather data for {location}, generating random data")
|
58
|
+
weather = {
|
59
|
+
"temperature": round(random.uniform(15, 30), 1),
|
60
|
+
"condition": random.choice(["sunny", "cloudy", "clear", "partly cloudy"]),
|
61
|
+
"humidity": round(random.uniform(50, 90)),
|
62
|
+
"wind_speed": round(random.uniform(5, 20)),
|
63
|
+
"feels_like": round(random.uniform(15, 30), 1),
|
64
|
+
"precipitation_chance": round(random.uniform(0, 100))
|
65
|
+
}
|
66
|
+
|
67
|
+
# Create WeatherData instance
|
68
|
+
weather_data = WeatherData(
|
69
|
+
location=location.title(),
|
70
|
+
temperature=weather["temperature"],
|
71
|
+
condition=weather["condition"],
|
72
|
+
humidity=weather["humidity"],
|
73
|
+
wind_speed=weather["wind_speed"],
|
74
|
+
feels_like=weather["feels_like"],
|
75
|
+
precipitation_chance=weather["precipitation_chance"]
|
76
|
+
)
|
77
|
+
|
78
|
+
logger.info(f"Weather data retrieved: {weather_data}")
|
79
|
+
return weather_data.model_dump()
|
80
|
+
|
81
|
+
async def main():
|
82
|
+
# Get API token from environment variable
|
83
|
+
token = os.getenv("OPENROUTER_API_KEY")
|
84
|
+
if not token:
|
85
|
+
raise ValueError("Please set the OPENROUTER_API_KEY environment variable")
|
86
|
+
|
87
|
+
# Initialize the OpenRouter client
|
88
|
+
logger.info("Initializing OpenRouter client")
|
89
|
+
openrouter_client = OpenRouterClient(
|
90
|
+
token=token,
|
91
|
+
model=OpenRouterModel.GPT41
|
92
|
+
)
|
93
|
+
|
94
|
+
# Initialize the ToolClient
|
95
|
+
logger.info("Initializing ToolClient")
|
96
|
+
tool_client = ToolClient(openrouter_client)
|
97
|
+
|
98
|
+
# Register the weather tool
|
99
|
+
logger.info("Registering weather tool")
|
100
|
+
tool_client.register_tool(
|
101
|
+
name="get_weather",
|
102
|
+
description="Get the current weather for a location",
|
103
|
+
function=get_weather,
|
104
|
+
schema={
|
105
|
+
"type": "object",
|
106
|
+
"properties": {
|
107
|
+
"location": {
|
108
|
+
"type": "string",
|
109
|
+
"description": "The city name to get weather for"
|
110
|
+
}
|
111
|
+
},
|
112
|
+
"required": ["location"]
|
113
|
+
}
|
114
|
+
)
|
115
|
+
|
116
|
+
# Example 1: Get weather for a single location using tools and parse
|
117
|
+
logger.info("Getting weather for New York using tools and parse")
|
118
|
+
messages = [
|
119
|
+
{
|
120
|
+
"role": "user",
|
121
|
+
"content": "What's the current weather in New York? Use the get_weather tool and format the response according to the WeatherData model."
|
122
|
+
}
|
123
|
+
]
|
124
|
+
|
125
|
+
response = tool_client.parse(
|
126
|
+
messages=messages,
|
127
|
+
response_format=WeatherData,
|
128
|
+
timeout=30.0,
|
129
|
+
)
|
130
|
+
|
131
|
+
weather_data = response.choices[0].message.parsed
|
132
|
+
print("\nWeather data for New York:")
|
133
|
+
print(f"Location: {weather_data.location}")
|
134
|
+
print(f"Temperature: {weather_data.temperature}°C")
|
135
|
+
print(f"Condition: {weather_data.condition}")
|
136
|
+
print(f"Humidity: {weather_data.humidity}%")
|
137
|
+
print(f"Wind Speed: {weather_data.wind_speed} km/h")
|
138
|
+
print(f"Feels Like: {weather_data.feels_like}°C")
|
139
|
+
print(f"Precipitation Chance: {weather_data.precipitation_chance}%")
|
140
|
+
|
141
|
+
|
142
|
+
if __name__ == "__main__":
|
143
|
+
import asyncio
|
144
|
+
asyncio.run(main())
|
@@ -0,0 +1,126 @@
|
|
1
|
+
"""
|
2
|
+
Example script demonstrating how to use the ToolClient with a custom Weather Tool.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import os
|
6
|
+
import logging
|
7
|
+
import random
|
8
|
+
from typing import Any
|
9
|
+
from pydantic import BaseModel
|
10
|
+
from mbxai.openrouter.client import OpenRouterClient, OpenRouterModel
|
11
|
+
from mbxai.tools.client import ToolClient
|
12
|
+
|
13
|
+
# Configure logging
|
14
|
+
logging.basicConfig(
|
15
|
+
level=logging.INFO,
|
16
|
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
17
|
+
)
|
18
|
+
logger = logging.getLogger(__name__)
|
19
|
+
|
20
|
+
# Define the weather data model
|
21
|
+
class WeatherData(BaseModel):
|
22
|
+
"""Weather data for a location."""
|
23
|
+
location: str
|
24
|
+
temperature: float
|
25
|
+
condition: str
|
26
|
+
humidity: float
|
27
|
+
wind_speed: float
|
28
|
+
|
29
|
+
# Mock weather data for demonstration
|
30
|
+
WEATHER_DATA = {
|
31
|
+
"new york": {"temperature": 22.5, "condition": "sunny", "humidity": 65, "wind_speed": 12},
|
32
|
+
"london": {"temperature": 18.2, "condition": "cloudy", "humidity": 75, "wind_speed": 8},
|
33
|
+
"tokyo": {"temperature": 25.7, "condition": "clear", "humidity": 60, "wind_speed": 5},
|
34
|
+
"paris": {"temperature": 20.1, "condition": "partly cloudy", "humidity": 70, "wind_speed": 10},
|
35
|
+
}
|
36
|
+
|
37
|
+
def get_weather(location: str) -> dict[str, Any]:
|
38
|
+
"""Get weather information for a location.
|
39
|
+
|
40
|
+
Args:
|
41
|
+
location: The city name to get weather for
|
42
|
+
|
43
|
+
Returns:
|
44
|
+
Weather information including temperature, condition, humidity, and wind speed
|
45
|
+
"""
|
46
|
+
logger.info(f"Getting weather for location: {location}")
|
47
|
+
|
48
|
+
# Convert location to lowercase for case-insensitive matching
|
49
|
+
location = location.lower()
|
50
|
+
|
51
|
+
# Get weather data or generate random data for unknown locations
|
52
|
+
if location in WEATHER_DATA:
|
53
|
+
weather = WEATHER_DATA[location]
|
54
|
+
else:
|
55
|
+
logger.warning(f"No weather data for {location}, generating random data")
|
56
|
+
weather = {
|
57
|
+
"temperature": round(random.uniform(15, 30), 1),
|
58
|
+
"condition": random.choice(["sunny", "cloudy", "clear", "partly cloudy"]),
|
59
|
+
"humidity": round(random.uniform(50, 90)),
|
60
|
+
"wind_speed": round(random.uniform(5, 20))
|
61
|
+
}
|
62
|
+
|
63
|
+
# Create WeatherData instance
|
64
|
+
weather_data = WeatherData(
|
65
|
+
location=location.title(),
|
66
|
+
temperature=weather["temperature"],
|
67
|
+
condition=weather["condition"],
|
68
|
+
humidity=weather["humidity"],
|
69
|
+
wind_speed=weather["wind_speed"]
|
70
|
+
)
|
71
|
+
|
72
|
+
logger.info(f"Weather data retrieved: {weather_data}")
|
73
|
+
return weather_data.model_dump()
|
74
|
+
|
75
|
+
async def main():
|
76
|
+
# Get API token from environment variable
|
77
|
+
token = os.getenv("OPENROUTER_API_KEY")
|
78
|
+
if not token:
|
79
|
+
raise ValueError("Please set the OPENROUTER_API_KEY environment variable")
|
80
|
+
|
81
|
+
# Initialize the OpenRouter client
|
82
|
+
logger.info("Initializing OpenRouter client")
|
83
|
+
openrouter_client = OpenRouterClient(
|
84
|
+
token=token,
|
85
|
+
model=OpenRouterModel.GPT35_TURBO
|
86
|
+
)
|
87
|
+
|
88
|
+
# Initialize the ToolClient
|
89
|
+
logger.info("Initializing ToolClient")
|
90
|
+
tool_client = ToolClient(openrouter_client)
|
91
|
+
|
92
|
+
# Register the weather tool
|
93
|
+
logger.info("Registering weather tool")
|
94
|
+
tool_client.register_tool(
|
95
|
+
name="get_weather",
|
96
|
+
description="Get the current weather for a location",
|
97
|
+
function=get_weather,
|
98
|
+
schema={
|
99
|
+
"type": "object",
|
100
|
+
"properties": {
|
101
|
+
"location": {
|
102
|
+
"type": "string",
|
103
|
+
"description": "The city name to get weather for"
|
104
|
+
}
|
105
|
+
},
|
106
|
+
"required": ["location"]
|
107
|
+
}
|
108
|
+
)
|
109
|
+
|
110
|
+
# Example 1: Simple weather query
|
111
|
+
logger.info("Sending weather query for New York")
|
112
|
+
messages = [
|
113
|
+
{"role": "user", "content": "What's the weather like in New York?"}
|
114
|
+
]
|
115
|
+
|
116
|
+
response = tool_client.chat(
|
117
|
+
messages,
|
118
|
+
timeout=30.0,
|
119
|
+
)
|
120
|
+
logger.info("Received response from model")
|
121
|
+
print("\nResponse for New York weather:")
|
122
|
+
print(response)
|
123
|
+
|
124
|
+
if __name__ == "__main__":
|
125
|
+
import asyncio
|
126
|
+
asyncio.run(main())
|
@@ -4,7 +4,6 @@ OpenRouter client implementation.
|
|
4
4
|
|
5
5
|
from typing import Any, Optional, Union
|
6
6
|
from openai import OpenAI, OpenAIError
|
7
|
-
from pydantic import BaseModel, TypeAdapter, Field
|
8
7
|
from .models import OpenRouterModel, OpenRouterModelRegistry
|
9
8
|
from .config import OpenRouterConfig
|
10
9
|
import logging
|
@@ -95,7 +94,7 @@ class OpenRouterClient:
|
|
95
94
|
def __init__(
|
96
95
|
self,
|
97
96
|
token: str,
|
98
|
-
model: Union[str, OpenRouterModel] = OpenRouterModel.
|
97
|
+
model: Union[str, OpenRouterModel] = OpenRouterModel.GPT35_TURBO,
|
99
98
|
base_url: Optional[str] = None,
|
100
99
|
default_headers: Optional[dict[str, str]] = None,
|
101
100
|
max_retries: int = 3,
|
@@ -106,7 +105,7 @@ class OpenRouterClient:
|
|
106
105
|
|
107
106
|
Args:
|
108
107
|
token: The OpenRouter API token
|
109
|
-
model: The model to use (default:
|
108
|
+
model: The model to use (default: GPT35_TURBO)
|
110
109
|
base_url: Optional custom base URL for the API
|
111
110
|
default_headers: Optional default headers for API requests
|
112
111
|
max_retries: Maximum number of retry attempts (default: 3)
|
@@ -187,7 +186,7 @@ class OpenRouterClient:
|
|
187
186
|
self.model = value
|
188
187
|
|
189
188
|
@with_retry()
|
190
|
-
def
|
189
|
+
def create(
|
191
190
|
self,
|
192
191
|
messages: list[dict[str, Any]],
|
193
192
|
*,
|
@@ -205,19 +204,15 @@ class OpenRouterClient:
|
|
205
204
|
total_size = sum(len(str(msg)) for msg in messages)
|
206
205
|
logger.info(f"Total message size: {total_size} bytes")
|
207
206
|
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
stream
|
207
|
+
request = {
|
208
|
+
"model": model or self.model,
|
209
|
+
"messages": messages,
|
210
|
+
"stream": stream,
|
212
211
|
**kwargs,
|
213
|
-
|
212
|
+
}
|
214
213
|
|
215
|
-
|
216
|
-
logger.info("Received response from OpenRouter")
|
217
|
-
if hasattr(response, 'output'):
|
218
|
-
logger.info(f"Response output length: {len(response.output) if response.output else 0}")
|
219
|
-
if hasattr(response, 'output_text'):
|
220
|
-
logger.info(f"Response output_text length: {len(response.output_text) if response.output_text else 0}")
|
214
|
+
response = self._client.chat.completions.create(**request)
|
215
|
+
logger.info(f"Received response from OpenRouter: {len(response.choices)} choices")
|
221
216
|
|
222
217
|
return response
|
223
218
|
|
@@ -235,31 +230,15 @@ class OpenRouterClient:
|
|
235
230
|
self._handle_api_error("chat completion", e)
|
236
231
|
|
237
232
|
@with_retry()
|
238
|
-
def
|
233
|
+
def parse(
|
239
234
|
self,
|
240
235
|
messages: list[dict[str, Any]],
|
241
|
-
response_format:
|
236
|
+
response_format: object,
|
242
237
|
*,
|
243
|
-
model:
|
238
|
+
model: str | None = None,
|
244
239
|
**kwargs: Any,
|
245
240
|
) -> Any:
|
246
|
-
"""
|
247
|
-
|
248
|
-
Args:
|
249
|
-
messages: list of messages
|
250
|
-
response_format: Pydantic model to parse the response into
|
251
|
-
model: Optional model override
|
252
|
-
**kwargs: Additional parameters
|
253
|
-
|
254
|
-
Returns:
|
255
|
-
Parsed completion response with output and output_parsed fields
|
256
|
-
|
257
|
-
Raises:
|
258
|
-
OpenRouterConnectionError: For connection issues
|
259
|
-
OpenRouterAPIError: For API errors
|
260
|
-
OpenRouterError: For other errors
|
261
|
-
ValueError: If response parsing fails
|
262
|
-
"""
|
241
|
+
"""Get a chat completion from OpenRouter."""
|
263
242
|
try:
|
264
243
|
# Log the request details
|
265
244
|
logger.info(f"Sending chat completion request to OpenRouter with model: {model or self.model}")
|
@@ -269,26 +248,15 @@ class OpenRouterClient:
|
|
269
248
|
total_size = sum(len(str(msg)) for msg in messages)
|
270
249
|
logger.info(f"Total message size: {total_size} bytes")
|
271
250
|
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
text_format=response_format,
|
251
|
+
request = {
|
252
|
+
"model": model or self.model,
|
253
|
+
"messages": messages,
|
254
|
+
"response_format": response_format,
|
277
255
|
**kwargs,
|
278
|
-
|
279
|
-
|
280
|
-
if not response:
|
281
|
-
logger.error(f"Full response content: {response}")
|
282
|
-
raise OpenRouterAPIError("Invalid response from OpenRouter: empty response")
|
256
|
+
}
|
283
257
|
|
284
|
-
|
285
|
-
logger.info("Received response from OpenRouter")
|
286
|
-
if hasattr(response, 'output'):
|
287
|
-
logger.info(f"Response output length: {len(response.output) if response.output else 0}")
|
288
|
-
if hasattr(response, 'output_parsed'):
|
289
|
-
logger.info("Response includes parsed output")
|
290
|
-
if hasattr(response, 'tool_calls'):
|
291
|
-
logger.info(f"Response includes {len(response.tool_calls)} tool calls")
|
258
|
+
response = self._client.beta.chat.completions.parse(**request)
|
259
|
+
logger.info(f"Received response from OpenRouter: {len(response.choices)} choices")
|
292
260
|
|
293
261
|
return response
|
294
262
|
|
@@ -305,45 +273,6 @@ class OpenRouterClient:
|
|
305
273
|
logger.error("Could not read response content")
|
306
274
|
self._handle_api_error("chat completion", e)
|
307
275
|
|
308
|
-
@with_retry()
|
309
|
-
def embeddings(
|
310
|
-
self,
|
311
|
-
input: Union[str, list[str]],
|
312
|
-
*,
|
313
|
-
model: Optional[Union[str, OpenRouterModel]] = None,
|
314
|
-
**kwargs: Any,
|
315
|
-
) -> Any:
|
316
|
-
"""Create embeddings.
|
317
|
-
|
318
|
-
Args:
|
319
|
-
input: Text to embed
|
320
|
-
model: Optional model override
|
321
|
-
**kwargs: Additional parameters
|
322
|
-
|
323
|
-
Returns:
|
324
|
-
Embeddings response
|
325
|
-
|
326
|
-
Raises:
|
327
|
-
OpenRouterConnectionError: For connection issues
|
328
|
-
OpenRouterAPIError: For API errors
|
329
|
-
OpenRouterError: For other errors
|
330
|
-
"""
|
331
|
-
try:
|
332
|
-
# Remove any incompatible parameters
|
333
|
-
kwargs.pop("parse", None) # Remove parse parameter if present
|
334
|
-
|
335
|
-
# Use text-embedding-ada-002 for embeddings
|
336
|
-
embeddings_model = "openai/text-embedding-ada-002"
|
337
|
-
|
338
|
-
return self._client.embeddings.create(
|
339
|
-
model=str(model or embeddings_model),
|
340
|
-
input=input if isinstance(input, list) else [input],
|
341
|
-
encoding_format="float", # Use float format instead of base64
|
342
|
-
**kwargs,
|
343
|
-
)
|
344
|
-
except Exception as e:
|
345
|
-
self._handle_api_error("embeddings", e)
|
346
|
-
|
347
276
|
@classmethod
|
348
277
|
def register_model(cls, name: str, value: str) -> None:
|
349
278
|
"""Register a new model.
|