solana-agent 27.5.0__py3-none-any.whl → 28.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- solana_agent/adapters/openai_adapter.py +45 -21
- solana_agent/factories/agent_factory.py +31 -86
- solana_agent/interfaces/providers/llm.py +1 -1
- solana_agent/services/agent.py +421 -311
- {solana_agent-27.5.0.dist-info → solana_agent-28.1.0.dist-info}/METADATA +35 -43
- {solana_agent-27.5.0.dist-info → solana_agent-28.1.0.dist-info}/RECORD +8 -8
- {solana_agent-27.5.0.dist-info → solana_agent-28.1.0.dist-info}/LICENSE +0 -0
- {solana_agent-27.5.0.dist-info → solana_agent-28.1.0.dist-info}/WHEEL +0 -0
@@ -10,12 +10,13 @@ from openai import AsyncOpenAI
|
|
10
10
|
from pydantic import BaseModel
|
11
11
|
import instructor
|
12
12
|
from instructor import Mode
|
13
|
+
import logfire
|
13
14
|
|
14
15
|
from solana_agent.interfaces.providers.llm import LLMProvider
|
15
16
|
|
16
17
|
T = TypeVar("T", bound=BaseModel)
|
17
18
|
|
18
|
-
DEFAULT_CHAT_MODEL = "gpt-4.1
|
19
|
+
DEFAULT_CHAT_MODEL = "gpt-4.1"
|
19
20
|
DEFAULT_PARSE_MODEL = "gpt-4.1-nano"
|
20
21
|
DEFAULT_EMBEDDING_MODEL = "text-embedding-3-large"
|
21
22
|
DEFAULT_EMBEDDING_DIMENSIONS = 3072
|
@@ -26,8 +27,21 @@ DEFAULT_TTS_MODEL = "tts-1"
|
|
26
27
|
class OpenAIAdapter(LLMProvider):
|
27
28
|
"""OpenAI implementation of LLMProvider with web search capabilities."""
|
28
29
|
|
29
|
-
def __init__(self, api_key: str):
|
30
|
+
def __init__(self, api_key: str, logfire_api_key: Optional[str] = None):
|
30
31
|
self.client = AsyncOpenAI(api_key=api_key)
|
32
|
+
|
33
|
+
self.logfire = False
|
34
|
+
if logfire_api_key:
|
35
|
+
try:
|
36
|
+
logfire.configure(token=logfire_api_key)
|
37
|
+
self.logfire = True
|
38
|
+
print("Logfire configured successfully.") # Optional: confirmation log
|
39
|
+
except Exception as e:
|
40
|
+
print(
|
41
|
+
f"Failed to configure Logfire: {e}"
|
42
|
+
) # Log error if configuration fails
|
43
|
+
self.logfire = False # Ensure logfire is False if config fails
|
44
|
+
|
31
45
|
self.parse_model = DEFAULT_PARSE_MODEL
|
32
46
|
self.text_model = DEFAULT_CHAT_MODEL
|
33
47
|
self.transcription_model = DEFAULT_TRANSCRIPTION_MODEL
|
@@ -65,6 +79,7 @@ class OpenAIAdapter(LLMProvider):
|
|
65
79
|
Audio bytes as they become available
|
66
80
|
"""
|
67
81
|
try:
|
82
|
+
logfire.instrument_openai(self.client)
|
68
83
|
async with self.client.audio.speech.with_streaming_response.create(
|
69
84
|
model=self.tts_model,
|
70
85
|
voice=voice,
|
@@ -106,6 +121,7 @@ class OpenAIAdapter(LLMProvider):
|
|
106
121
|
Transcript text chunks as they become available
|
107
122
|
"""
|
108
123
|
try:
|
124
|
+
logfire.instrument_openai(self.client)
|
109
125
|
async with self.client.audio.transcriptions.with_streaming_response.create(
|
110
126
|
model=self.transcription_model,
|
111
127
|
file=(f"file.{input_format}", audio_bytes),
|
@@ -129,45 +145,44 @@ class OpenAIAdapter(LLMProvider):
|
|
129
145
|
api_key: Optional[str] = None,
|
130
146
|
base_url: Optional[str] = None,
|
131
147
|
model: Optional[str] = None,
|
132
|
-
) ->
|
133
|
-
"""Generate text from OpenAI models."""
|
148
|
+
) -> str: # pragma: no cover
|
149
|
+
"""Generate text from OpenAI models as a single string."""
|
134
150
|
messages = []
|
135
|
-
|
136
151
|
if system_prompt:
|
137
152
|
messages.append({"role": "system", "content": system_prompt})
|
138
|
-
|
139
153
|
messages.append({"role": "user", "content": prompt})
|
140
154
|
|
141
|
-
# Prepare request parameters
|
155
|
+
# Prepare request parameters - stream is always False now
|
142
156
|
request_params = {
|
143
157
|
"messages": messages,
|
144
|
-
"stream":
|
145
|
-
"model": self.text_model,
|
158
|
+
"stream": False, # Hardcoded to False
|
159
|
+
"model": model or self.text_model,
|
146
160
|
}
|
147
161
|
|
162
|
+
# Determine client based on provided api_key/base_url
|
148
163
|
if api_key and base_url:
|
149
164
|
client = AsyncOpenAI(api_key=api_key, base_url=base_url)
|
150
165
|
else:
|
151
166
|
client = self.client
|
152
167
|
|
153
|
-
if
|
154
|
-
|
168
|
+
if self.logfire:
|
169
|
+
logfire.instrument_openai(client)
|
155
170
|
|
156
171
|
try:
|
172
|
+
# Make the non-streaming API call
|
157
173
|
response = await client.chat.completions.create(**request_params)
|
158
174
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
175
|
+
# Handle non-streaming response
|
176
|
+
if response.choices and response.choices[0].message.content:
|
177
|
+
full_text = response.choices[0].message.content
|
178
|
+
return full_text # Return the complete string
|
179
|
+
else:
|
180
|
+
print("Received non-streaming response with no content.")
|
181
|
+
return "" # Return empty string if no content
|
164
182
|
|
165
183
|
except Exception as e:
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
print(traceback.format_exc())
|
170
|
-
yield f"I apologize, but I encountered an error: {str(e)}"
|
184
|
+
# Log the error and return an error message string
|
185
|
+
print(f"Error in generate_text: {e}")
|
171
186
|
|
172
187
|
async def parse_structured_output(
|
173
188
|
self,
|
@@ -190,6 +205,9 @@ class OpenAIAdapter(LLMProvider):
|
|
190
205
|
else:
|
191
206
|
client = self.client
|
192
207
|
|
208
|
+
if self.logfire:
|
209
|
+
logfire.instrument_openai(client)
|
210
|
+
|
193
211
|
if model:
|
194
212
|
self.parse_model = model
|
195
213
|
|
@@ -233,6 +251,9 @@ class OpenAIAdapter(LLMProvider):
|
|
233
251
|
else:
|
234
252
|
client = self.client
|
235
253
|
|
254
|
+
if self.logfire:
|
255
|
+
logfire.instrument_openai(client)
|
256
|
+
|
236
257
|
if model:
|
237
258
|
self.parse_model = model
|
238
259
|
|
@@ -292,6 +313,9 @@ class OpenAIAdapter(LLMProvider):
|
|
292
313
|
# Replace newlines with spaces as recommended by OpenAI
|
293
314
|
text = text.replace("\n", " ")
|
294
315
|
|
316
|
+
if self.logfire:
|
317
|
+
logfire.instrument_openai(self.client)
|
318
|
+
|
295
319
|
response = await self.client.embeddings.create(
|
296
320
|
input=[text], model=embedding_model, dimensions=embedding_dimensions
|
297
321
|
)
|
@@ -78,6 +78,10 @@ class SolanaAgentFactory:
|
|
78
78
|
# Create adapters
|
79
79
|
|
80
80
|
if "mongo" in config:
|
81
|
+
if "connection_string" not in config["mongo"]:
|
82
|
+
raise ValueError("MongoDB connection string is required.")
|
83
|
+
if "database" not in config["mongo"]:
|
84
|
+
raise ValueError("MongoDB database name is required.")
|
81
85
|
db_adapter = MongoDBAdapter(
|
82
86
|
connection_string=config["mongo"]["connection_string"],
|
83
87
|
database_name=config["mongo"]["database"],
|
@@ -85,9 +89,21 @@ class SolanaAgentFactory:
|
|
85
89
|
else:
|
86
90
|
db_adapter = None
|
87
91
|
|
88
|
-
|
89
|
-
api_key
|
90
|
-
|
92
|
+
if "logfire" in config:
|
93
|
+
if "api_key" not in config["logfire"]:
|
94
|
+
raise ValueError("Pydantic Logfire API key is required.")
|
95
|
+
if "openai" not in config or "api_key" not in config["openai"]:
|
96
|
+
raise ValueError("OpenAI API key is required.")
|
97
|
+
llm_adapter = OpenAIAdapter(
|
98
|
+
api_key=config["openai"]["api_key"],
|
99
|
+
logfire_api_key=config["logfire"].get("api_key"),
|
100
|
+
)
|
101
|
+
else:
|
102
|
+
if "openai" not in config or "api_key" not in config["openai"]:
|
103
|
+
raise ValueError("OpenAI API key is required.")
|
104
|
+
llm_adapter = OpenAIAdapter(
|
105
|
+
api_key=config["openai"].get("api_key"),
|
106
|
+
)
|
91
107
|
|
92
108
|
# Create business mission if specified in config
|
93
109
|
business_mission = None
|
@@ -130,90 +146,19 @@ class SolanaAgentFactory:
|
|
130
146
|
f"Loaded {len(input_guardrails)} input guardrails and {len(output_guardrails)} output guardrails."
|
131
147
|
)
|
132
148
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
llm_provider=llm_adapter,
|
141
|
-
business_mission=business_mission,
|
142
|
-
config=config,
|
143
|
-
api_key=config["gemini"]["api_key"],
|
144
|
-
base_url="https://generativelanguage.googleapis.com/v1beta/openai/",
|
145
|
-
model="gemini-2.5-flash-preview-04-17",
|
146
|
-
output_guardrails=output_guardrails,
|
147
|
-
)
|
148
|
-
|
149
|
-
# Create routing service
|
150
|
-
routing_service = RoutingService(
|
151
|
-
llm_provider=llm_adapter,
|
152
|
-
agent_service=agent_service,
|
153
|
-
api_key=config["gemini"]["api_key"],
|
154
|
-
base_url="https://generativelanguage.googleapis.com/v1beta/openai/",
|
155
|
-
model="gemini-2.5-flash-preview-04-17",
|
156
|
-
)
|
157
|
-
|
158
|
-
elif (
|
159
|
-
"gemini" in config
|
160
|
-
and "api_key" in config["gemini"]
|
161
|
-
and "grok" in config
|
162
|
-
and "api_key" in config["grok"]
|
163
|
-
):
|
164
|
-
# Create primary services
|
165
|
-
agent_service = AgentService(
|
166
|
-
llm_provider=llm_adapter,
|
167
|
-
business_mission=business_mission,
|
168
|
-
config=config,
|
169
|
-
api_key=config["grok"]["api_key"],
|
170
|
-
base_url="https://api.x.ai/v1",
|
171
|
-
model="grok-3-mini-fast-beta",
|
172
|
-
output_guardrails=output_guardrails,
|
173
|
-
)
|
174
|
-
# Create routing service
|
175
|
-
routing_service = RoutingService(
|
176
|
-
llm_provider=llm_adapter,
|
177
|
-
agent_service=agent_service,
|
178
|
-
api_key=config["gemini"]["api_key"],
|
179
|
-
base_url="https://generativelanguage.googleapis.com/v1beta/openai/",
|
180
|
-
model="gemini-2.5-flash-preview-04-17",
|
181
|
-
)
|
182
|
-
|
183
|
-
elif (
|
184
|
-
"grok" in config and "api_key" in config["grok"] and "gemini" not in config
|
185
|
-
):
|
186
|
-
# Create primary services
|
187
|
-
agent_service = AgentService(
|
188
|
-
llm_provider=llm_adapter,
|
189
|
-
business_mission=business_mission,
|
190
|
-
config=config,
|
191
|
-
api_key=config["grok"]["api_key"],
|
192
|
-
base_url="https://api.x.ai/v1",
|
193
|
-
model="grok-3-mini-fast-beta",
|
194
|
-
output_guardrails=output_guardrails,
|
195
|
-
)
|
196
|
-
|
197
|
-
# Create routing service
|
198
|
-
routing_service = RoutingService(
|
199
|
-
llm_provider=llm_adapter,
|
200
|
-
agent_service=agent_service,
|
201
|
-
)
|
202
|
-
|
203
|
-
else:
|
204
|
-
# Create primary services
|
205
|
-
agent_service = AgentService(
|
206
|
-
llm_provider=llm_adapter,
|
207
|
-
business_mission=business_mission,
|
208
|
-
config=config,
|
209
|
-
output_guardrails=output_guardrails,
|
210
|
-
)
|
149
|
+
# Create primary services
|
150
|
+
agent_service = AgentService(
|
151
|
+
llm_provider=llm_adapter,
|
152
|
+
business_mission=business_mission,
|
153
|
+
config=config,
|
154
|
+
output_guardrails=output_guardrails,
|
155
|
+
)
|
211
156
|
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
157
|
+
# Create routing service
|
158
|
+
routing_service = RoutingService(
|
159
|
+
llm_provider=llm_adapter,
|
160
|
+
agent_service=agent_service,
|
161
|
+
)
|
217
162
|
|
218
163
|
# Debug the agent service tool registry
|
219
164
|
print(
|