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.
@@ -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-mini"
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
- ) -> AsyncGenerator[str, None]: # pragma: no cover
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": True,
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 model:
154
- request_params["model"] = model
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
- async for chunk in response:
160
- if chunk.choices:
161
- if chunk.choices[0].delta.content:
162
- text = chunk.choices[0].delta.content
163
- yield text
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
- print(f"Error in generate_text: {str(e)}")
167
- import traceback
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
- llm_adapter = OpenAIAdapter(
89
- api_key=config["openai"]["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
- if (
134
- "gemini" in config
135
- and "api_key" in config["gemini"]
136
- and "grok" not in config
137
- ):
138
- # Create primary services
139
- agent_service = AgentService(
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
- # Create routing service
213
- routing_service = RoutingService(
214
- llm_provider=llm_adapter,
215
- agent_service=agent_service,
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(
@@ -25,7 +25,7 @@ class LLMProvider(ABC):
25
25
  api_key: Optional[str] = None,
26
26
  base_url: Optional[str] = None,
27
27
  model: Optional[str] = None,
28
- ) -> AsyncGenerator[str, None]:
28
+ ) -> str:
29
29
  """Generate text from the language model."""
30
30
  pass
31
31