solana-agent 24.1.1__tar.gz → 25.0.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.
Files changed (35) hide show
  1. {solana_agent-24.1.1 → solana_agent-25.0.0}/PKG-INFO +31 -60
  2. {solana_agent-24.1.1 → solana_agent-25.0.0}/README.md +29 -59
  3. {solana_agent-24.1.1 → solana_agent-25.0.0}/pyproject.toml +2 -1
  4. {solana_agent-24.1.1 → solana_agent-25.0.0}/solana_agent/adapters/llm_adapter.py +88 -11
  5. {solana_agent-24.1.1 → solana_agent-25.0.0}/solana_agent/factories/agent_factory.py +32 -12
  6. {solana_agent-24.1.1 → solana_agent-25.0.0}/solana_agent/interfaces/providers/llm.py +9 -11
  7. {solana_agent-24.1.1 → solana_agent-25.0.0}/solana_agent/interfaces/services/agent.py +0 -3
  8. {solana_agent-24.1.1 → solana_agent-25.0.0}/solana_agent/services/agent.py +15 -14
  9. {solana_agent-24.1.1 → solana_agent-25.0.0}/solana_agent/services/query.py +1 -2
  10. {solana_agent-24.1.1 → solana_agent-25.0.0}/solana_agent/services/routing.py +9 -0
  11. {solana_agent-24.1.1 → solana_agent-25.0.0}/LICENSE +0 -0
  12. {solana_agent-24.1.1 → solana_agent-25.0.0}/solana_agent/__init__.py +0 -0
  13. {solana_agent-24.1.1 → solana_agent-25.0.0}/solana_agent/adapters/__init__.py +0 -0
  14. {solana_agent-24.1.1 → solana_agent-25.0.0}/solana_agent/adapters/mongodb_adapter.py +0 -0
  15. {solana_agent-24.1.1 → solana_agent-25.0.0}/solana_agent/client/__init__.py +0 -0
  16. {solana_agent-24.1.1 → solana_agent-25.0.0}/solana_agent/client/solana_agent.py +0 -0
  17. {solana_agent-24.1.1 → solana_agent-25.0.0}/solana_agent/domains/__init__.py +0 -0
  18. {solana_agent-24.1.1 → solana_agent-25.0.0}/solana_agent/domains/agent.py +0 -0
  19. {solana_agent-24.1.1 → solana_agent-25.0.0}/solana_agent/domains/routing.py +0 -0
  20. {solana_agent-24.1.1 → solana_agent-25.0.0}/solana_agent/factories/__init__.py +0 -0
  21. {solana_agent-24.1.1 → solana_agent-25.0.0}/solana_agent/interfaces/__init__.py +0 -0
  22. {solana_agent-24.1.1 → solana_agent-25.0.0}/solana_agent/interfaces/client/client.py +0 -0
  23. {solana_agent-24.1.1 → solana_agent-25.0.0}/solana_agent/interfaces/plugins/plugins.py +0 -0
  24. {solana_agent-24.1.1 → solana_agent-25.0.0}/solana_agent/interfaces/providers/data_storage.py +0 -0
  25. {solana_agent-24.1.1 → solana_agent-25.0.0}/solana_agent/interfaces/providers/memory.py +0 -0
  26. {solana_agent-24.1.1 → solana_agent-25.0.0}/solana_agent/interfaces/services/query.py +0 -0
  27. {solana_agent-24.1.1 → solana_agent-25.0.0}/solana_agent/interfaces/services/routing.py +0 -0
  28. {solana_agent-24.1.1 → solana_agent-25.0.0}/solana_agent/plugins/__init__.py +0 -0
  29. {solana_agent-24.1.1 → solana_agent-25.0.0}/solana_agent/plugins/manager.py +0 -0
  30. {solana_agent-24.1.1 → solana_agent-25.0.0}/solana_agent/plugins/registry.py +0 -0
  31. {solana_agent-24.1.1 → solana_agent-25.0.0}/solana_agent/plugins/tools/__init__.py +0 -0
  32. {solana_agent-24.1.1 → solana_agent-25.0.0}/solana_agent/plugins/tools/auto_tool.py +0 -0
  33. {solana_agent-24.1.1 → solana_agent-25.0.0}/solana_agent/repositories/__init__.py +0 -0
  34. {solana_agent-24.1.1 → solana_agent-25.0.0}/solana_agent/repositories/memory.py +0 -0
  35. {solana_agent-24.1.1 → solana_agent-25.0.0}/solana_agent/services/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: solana-agent
3
- Version: 24.1.1
3
+ Version: 25.0.0
4
4
  Summary: Agentic IQ
5
5
  License: MIT
6
6
  Keywords: ai,openai,ai agents,agi
@@ -14,6 +14,7 @@ Classifier: Programming Language :: Python :: 3
14
14
  Classifier: Programming Language :: Python :: 3.12
15
15
  Classifier: Programming Language :: Python :: 3.13
16
16
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
17
+ Requires-Dist: instructor (>=1.7.9,<2.0.0)
17
18
  Requires-Dist: openai (>=1.71.0,<2.0.0)
18
19
  Requires-Dist: pydantic (>=2.11.2,<3.0.0)
19
20
  Requires-Dist: pymongo (>=4.11.3,<5.0.0)
@@ -75,16 +76,17 @@ Build your AI business in three lines of code!
75
76
  ### Tech
76
77
 
77
78
  * [Python](https://python.org) - Programming Language
78
- * [OpenAI](https://openai.com) - LLM Provider
79
+ * [OpenAI](https://openai.com) & [Gemini](https://aistudio.google.com/) - LLM Providers
79
80
  * [MongoDB](https://mongodb.com) - Conversational History (optional)
80
81
  * [Zep Cloud](https://getzep.com) - Conversational Memory (optional)
81
82
 
82
83
  ### LLMs
83
84
 
84
- * [gpt-4o-mini](https://platform.openai.com/docs/models/gpt-4o-mini)
85
+ * [gpt-4o-mini](https://platform.openai.com/docs/models/gpt-4o-mini) or [gemini-2.0-flash](https://ai.google.dev/gemini-api/docs/models#gemini-2.0-flash)
85
86
  * [gpt-4o-mini-tts](https://platform.openai.com/docs/models/gpt-4o-mini-tts)
86
87
  * [gpt-4o-mini-transcribe](https://platform.openai.com/docs/models/gpt-4o-mini-transcribe)
87
88
 
89
+
88
90
  ## Installation
89
91
 
90
92
  You can install Solana Agent using pip:
@@ -144,6 +146,9 @@ Keep this in mind while designing your agentic systems using Solana Agent.
144
146
  from solana_agent import SolanaAgent
145
147
 
146
148
  config = {
149
+ "gemini": {
150
+ "api_key": "your-gemini-api-key",
151
+ },
147
152
  "openai": {
148
153
  "api_key": "your-openai-api-key",
149
154
  },
@@ -167,24 +172,15 @@ async for response in solana_agent.process("user123", "What are the latest AI de
167
172
  print(response, end="")
168
173
  ```
169
174
 
170
- Single Agent:
171
-
172
- * OpenAI API calls with no tool call = 1 (agent)
173
-
174
- * OpenAI API calls with tool call = 2 (agent, agent)
175
-
176
- Multiple Agents:
177
-
178
- * OpenAI API calls with no tool call = 2 (router, agent)
179
-
180
- * OpenAI API calls with tool call = 3 (router, agent, agent)
181
-
182
175
  ### Audio/Audio Streaming
183
176
 
184
177
  ```python
185
178
  from solana_agent import SolanaAgent
186
179
 
187
180
  config = {
181
+ "gemini": {
182
+ "api_key": "your-gemini-api-key",
183
+ },
188
184
  "openai": {
189
185
  "api_key": "your-openai-api-key",
190
186
  },
@@ -210,25 +206,15 @@ async for response in solana_agent.process("user123", audio_content, output_form
210
206
  print(response, end="")
211
207
  ```
212
208
 
213
- Single Agent:
214
-
215
- * OpenAI API calls with no tool call = 3 (audio transcribe, agent, TTS)
216
-
217
- * OpenAI API calls with tool call = 4 (audio transcribe, agent, agent, TTS)
218
-
219
- Multiple Agents:
220
-
221
- * OpenAI API calls with no tool call = 4 (router, audio transcribe, agent, TTS)
222
-
223
- * OpenAI API calls with tool call = 5 (router, audio transcribe, agent, agent, TTS)
224
-
225
-
226
209
  ### Text/Audio Streaming
227
210
 
228
211
  ```python
229
212
  from solana_agent import SolanaAgent
230
213
 
231
214
  config = {
215
+ "gemini": {
216
+ "api_key": "your-gemini-api-key",
217
+ },
232
218
  "openai": {
233
219
  "api_key": "your-openai-api-key",
234
220
  },
@@ -252,24 +238,15 @@ async for response in solana_agent.process("user123", "What is the latest news o
252
238
  print(response, end="")
253
239
  ```
254
240
 
255
- Single Agent:
256
-
257
- * OpenAI API calls with no tool call = 2 (agent, TTS)
258
-
259
- * OpenAI API calls with tool call = 3 (agent, agent, TTS)
260
-
261
- Multiple Agents:
262
-
263
- * OpenAI API calls with no tool call = 3 (router, agent, TTS)
264
-
265
- * OpenAI API calls with tool call = 4 (router, agent, agent, TTS)
266
-
267
241
  ### Audio/Text Streaming
268
242
 
269
243
  ```python
270
244
  from solana_agent import SolanaAgent
271
245
 
272
246
  config = {
247
+ "gemini": {
248
+ "api_key": "your-gemini-api-key",
249
+ },
273
250
  "openai": {
274
251
  "api_key": "your-openai-api-key",
275
252
  },
@@ -295,18 +272,6 @@ async for response in solana_agent.process("user123", audio_content, audio_input
295
272
  print(response, end="")
296
273
  ```
297
274
 
298
- Single Agent:
299
-
300
- * OpenAI API calls with no tool call = 2 (audio transcribe, agent)
301
-
302
- * OpenAI API calls with tool call = 3 (audio transcribe, agent, agent)
303
-
304
- Multiple Agents:
305
-
306
- * OpenAI API calls with no tool call = 3 (router, audio transcribe, agent)
307
-
308
- * OpenAI API calls with tool call = 4 (router, audio transcribe, agent, agent)
309
-
310
275
  ## Optional Feature Configs
311
276
 
312
277
  ### Business Alignment
@@ -332,8 +297,8 @@ config = {
332
297
  ```python
333
298
  config = {
334
299
  "mongo": {
335
- "connection_string": "mongodb://localhost:27017",
336
- "database": "solana_agent"
300
+ "connection_string": "your-mongo-connection-string",
301
+ "database": "your-database-name"
337
302
  },
338
303
  }
339
304
  ```
@@ -348,12 +313,6 @@ config = {
348
313
  }
349
314
  ```
350
315
 
351
- API Calls:
352
-
353
- * Zep adds 2 API calls per user query (GET and POST)
354
-
355
- * If the Zep user and session isn't created it creates them for 2 API calls (POST)
356
-
357
316
  ### Customize Speech
358
317
 
359
318
  This is an audio to audio example using the `audio_instructions` parameter.
@@ -390,6 +349,9 @@ Tools can be used from plugins like Solana Agent Kit (sakit) or via inline tools
390
349
  from solana_agent import SolanaAgent
391
350
 
392
351
  config = {
352
+ "gemini": {
353
+ "api_key": "your-gemini-api-key",
354
+ },
393
355
  "openai": {
394
356
  "api_key": "your-openai-api-key",
395
357
  },
@@ -472,6 +434,9 @@ class TestTool(Tool):
472
434
  }
473
435
 
474
436
  config = {
437
+ "gemini": {
438
+ "api_key": "your-gemini-api-key",
439
+ },
475
440
  "openai": {
476
441
  "api_key": "your-openai-api-key",
477
442
  },
@@ -511,6 +476,9 @@ This knowledge is accessible to all your AI agents.
511
476
  from solana_agent import SolanaAgent
512
477
 
513
478
  config = {
479
+ "gemini": {
480
+ "api_key": "your-gemini-api-key",
481
+ },
514
482
  "openai": {
515
483
  "api_key": "your-openai-api-key",
516
484
  },
@@ -538,6 +506,9 @@ from solana_agent import SolanaAgent
538
506
  from solana_agent.interfaces.services.routing import RoutingService as RoutingServiceInterface
539
507
 
540
508
  config = {
509
+ "gemini": {
510
+ "api_key": "your-gemini-api-key",
511
+ },
541
512
  "openai": {
542
513
  "api_key": "your-openai-api-key",
543
514
  },
@@ -51,16 +51,17 @@ Build your AI business in three lines of code!
51
51
  ### Tech
52
52
 
53
53
  * [Python](https://python.org) - Programming Language
54
- * [OpenAI](https://openai.com) - LLM Provider
54
+ * [OpenAI](https://openai.com) & [Gemini](https://aistudio.google.com/) - LLM Providers
55
55
  * [MongoDB](https://mongodb.com) - Conversational History (optional)
56
56
  * [Zep Cloud](https://getzep.com) - Conversational Memory (optional)
57
57
 
58
58
  ### LLMs
59
59
 
60
- * [gpt-4o-mini](https://platform.openai.com/docs/models/gpt-4o-mini)
60
+ * [gpt-4o-mini](https://platform.openai.com/docs/models/gpt-4o-mini) or [gemini-2.0-flash](https://ai.google.dev/gemini-api/docs/models#gemini-2.0-flash)
61
61
  * [gpt-4o-mini-tts](https://platform.openai.com/docs/models/gpt-4o-mini-tts)
62
62
  * [gpt-4o-mini-transcribe](https://platform.openai.com/docs/models/gpt-4o-mini-transcribe)
63
63
 
64
+
64
65
  ## Installation
65
66
 
66
67
  You can install Solana Agent using pip:
@@ -120,6 +121,9 @@ Keep this in mind while designing your agentic systems using Solana Agent.
120
121
  from solana_agent import SolanaAgent
121
122
 
122
123
  config = {
124
+ "gemini": {
125
+ "api_key": "your-gemini-api-key",
126
+ },
123
127
  "openai": {
124
128
  "api_key": "your-openai-api-key",
125
129
  },
@@ -143,24 +147,15 @@ async for response in solana_agent.process("user123", "What are the latest AI de
143
147
  print(response, end="")
144
148
  ```
145
149
 
146
- Single Agent:
147
-
148
- * OpenAI API calls with no tool call = 1 (agent)
149
-
150
- * OpenAI API calls with tool call = 2 (agent, agent)
151
-
152
- Multiple Agents:
153
-
154
- * OpenAI API calls with no tool call = 2 (router, agent)
155
-
156
- * OpenAI API calls with tool call = 3 (router, agent, agent)
157
-
158
150
  ### Audio/Audio Streaming
159
151
 
160
152
  ```python
161
153
  from solana_agent import SolanaAgent
162
154
 
163
155
  config = {
156
+ "gemini": {
157
+ "api_key": "your-gemini-api-key",
158
+ },
164
159
  "openai": {
165
160
  "api_key": "your-openai-api-key",
166
161
  },
@@ -186,25 +181,15 @@ async for response in solana_agent.process("user123", audio_content, output_form
186
181
  print(response, end="")
187
182
  ```
188
183
 
189
- Single Agent:
190
-
191
- * OpenAI API calls with no tool call = 3 (audio transcribe, agent, TTS)
192
-
193
- * OpenAI API calls with tool call = 4 (audio transcribe, agent, agent, TTS)
194
-
195
- Multiple Agents:
196
-
197
- * OpenAI API calls with no tool call = 4 (router, audio transcribe, agent, TTS)
198
-
199
- * OpenAI API calls with tool call = 5 (router, audio transcribe, agent, agent, TTS)
200
-
201
-
202
184
  ### Text/Audio Streaming
203
185
 
204
186
  ```python
205
187
  from solana_agent import SolanaAgent
206
188
 
207
189
  config = {
190
+ "gemini": {
191
+ "api_key": "your-gemini-api-key",
192
+ },
208
193
  "openai": {
209
194
  "api_key": "your-openai-api-key",
210
195
  },
@@ -228,24 +213,15 @@ async for response in solana_agent.process("user123", "What is the latest news o
228
213
  print(response, end="")
229
214
  ```
230
215
 
231
- Single Agent:
232
-
233
- * OpenAI API calls with no tool call = 2 (agent, TTS)
234
-
235
- * OpenAI API calls with tool call = 3 (agent, agent, TTS)
236
-
237
- Multiple Agents:
238
-
239
- * OpenAI API calls with no tool call = 3 (router, agent, TTS)
240
-
241
- * OpenAI API calls with tool call = 4 (router, agent, agent, TTS)
242
-
243
216
  ### Audio/Text Streaming
244
217
 
245
218
  ```python
246
219
  from solana_agent import SolanaAgent
247
220
 
248
221
  config = {
222
+ "gemini": {
223
+ "api_key": "your-gemini-api-key",
224
+ },
249
225
  "openai": {
250
226
  "api_key": "your-openai-api-key",
251
227
  },
@@ -271,18 +247,6 @@ async for response in solana_agent.process("user123", audio_content, audio_input
271
247
  print(response, end="")
272
248
  ```
273
249
 
274
- Single Agent:
275
-
276
- * OpenAI API calls with no tool call = 2 (audio transcribe, agent)
277
-
278
- * OpenAI API calls with tool call = 3 (audio transcribe, agent, agent)
279
-
280
- Multiple Agents:
281
-
282
- * OpenAI API calls with no tool call = 3 (router, audio transcribe, agent)
283
-
284
- * OpenAI API calls with tool call = 4 (router, audio transcribe, agent, agent)
285
-
286
250
  ## Optional Feature Configs
287
251
 
288
252
  ### Business Alignment
@@ -308,8 +272,8 @@ config = {
308
272
  ```python
309
273
  config = {
310
274
  "mongo": {
311
- "connection_string": "mongodb://localhost:27017",
312
- "database": "solana_agent"
275
+ "connection_string": "your-mongo-connection-string",
276
+ "database": "your-database-name"
313
277
  },
314
278
  }
315
279
  ```
@@ -324,12 +288,6 @@ config = {
324
288
  }
325
289
  ```
326
290
 
327
- API Calls:
328
-
329
- * Zep adds 2 API calls per user query (GET and POST)
330
-
331
- * If the Zep user and session isn't created it creates them for 2 API calls (POST)
332
-
333
291
  ### Customize Speech
334
292
 
335
293
  This is an audio to audio example using the `audio_instructions` parameter.
@@ -366,6 +324,9 @@ Tools can be used from plugins like Solana Agent Kit (sakit) or via inline tools
366
324
  from solana_agent import SolanaAgent
367
325
 
368
326
  config = {
327
+ "gemini": {
328
+ "api_key": "your-gemini-api-key",
329
+ },
369
330
  "openai": {
370
331
  "api_key": "your-openai-api-key",
371
332
  },
@@ -448,6 +409,9 @@ class TestTool(Tool):
448
409
  }
449
410
 
450
411
  config = {
412
+ "gemini": {
413
+ "api_key": "your-gemini-api-key",
414
+ },
451
415
  "openai": {
452
416
  "api_key": "your-openai-api-key",
453
417
  },
@@ -487,6 +451,9 @@ This knowledge is accessible to all your AI agents.
487
451
  from solana_agent import SolanaAgent
488
452
 
489
453
  config = {
454
+ "gemini": {
455
+ "api_key": "your-gemini-api-key",
456
+ },
490
457
  "openai": {
491
458
  "api_key": "your-openai-api-key",
492
459
  },
@@ -514,6 +481,9 @@ from solana_agent import SolanaAgent
514
481
  from solana_agent.interfaces.services.routing import RoutingService as RoutingServiceInterface
515
482
 
516
483
  config = {
484
+ "gemini": {
485
+ "api_key": "your-gemini-api-key",
486
+ },
517
487
  "openai": {
518
488
  "api_key": "your-openai-api-key",
519
489
  },
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "solana-agent"
3
- version = "24.1.1"
3
+ version = "25.0.0"
4
4
  description = "Agentic IQ"
5
5
  authors = ["Bevan Hunt <bevan@bevanhunt.com>"]
6
6
  license = "MIT"
@@ -27,6 +27,7 @@ openai = "^1.71.0"
27
27
  pydantic = "^2.11.2"
28
28
  pymongo = "^4.11.3"
29
29
  zep-cloud = "^2.9.0"
30
+ instructor = "^1.7.9"
30
31
 
31
32
  [tool.poetry.group.dev.dependencies]
32
33
  pytest = "^8.3.5"
@@ -3,10 +3,12 @@ LLM provider adapters for the Solana Agent system.
3
3
 
4
4
  These adapters implement the LLMProvider interface for different LLM services.
5
5
  """
6
- from typing import AsyncGenerator, Literal, Type, TypeVar
6
+ from typing import AsyncGenerator, Literal, Optional, Type, TypeVar
7
7
 
8
8
  from openai import AsyncOpenAI
9
9
  from pydantic import BaseModel
10
+ import instructor
11
+ from instructor import Mode
10
12
 
11
13
  from solana_agent.interfaces.providers.llm import LLMProvider
12
14
 
@@ -103,6 +105,9 @@ class OpenAIAdapter(LLMProvider):
103
105
  self,
104
106
  prompt: str,
105
107
  system_prompt: str = "",
108
+ api_key: Optional[str] = None,
109
+ base_url: Optional[str] = None,
110
+ model: Optional[str] = None,
106
111
  ) -> AsyncGenerator[str, None]: # pragma: no cover
107
112
  """Generate text from OpenAI models."""
108
113
  messages = []
@@ -118,8 +123,18 @@ class OpenAIAdapter(LLMProvider):
118
123
  "stream": True,
119
124
  "model": self.text_model,
120
125
  }
126
+
127
+ client = self.client
128
+
129
+ if api_key and base_url:
130
+ client.api_key = api_key
131
+ client.base_url = base_url
132
+
133
+ if model:
134
+ request_params["model"] = model
135
+
121
136
  try:
122
- response = await self.client.chat.completions.create(**request_params)
137
+ response = await client.chat.completions.create(**request_params)
123
138
 
124
139
  async for chunk in response:
125
140
  if chunk.choices:
@@ -138,21 +153,83 @@ class OpenAIAdapter(LLMProvider):
138
153
  prompt: str,
139
154
  system_prompt: str,
140
155
  model_class: Type[T],
156
+ api_key: Optional[str] = None,
157
+ base_url: Optional[str] = None,
158
+ model: Optional[str] = None,
141
159
  ) -> T: # pragma: no cover
142
- """Generate structured output using Pydantic model parsing."""
143
- messages = []
144
- if system_prompt:
145
- messages.append({"role": "system", "content": system_prompt})
160
+ """Generate structured output using Pydantic model parsing with Instructor."""
146
161
 
162
+ messages = []
163
+ messages.append({"role": "system", "content": system_prompt})
147
164
  messages.append({"role": "user", "content": prompt})
148
165
 
149
166
  try:
150
- # First try the beta parsing API
151
- completion = await self.client.beta.chat.completions.parse(
167
+ if api_key and base_url:
168
+ self.client.api_key = api_key
169
+ self.client.base_url = base_url
170
+
171
+ if model:
172
+ self.parse_model = model
173
+
174
+ # Create a patched client with TOOLS_STRICT mode
175
+ patched_client = instructor.from_openai(
176
+ self.client, mode=Mode.TOOLS_STRICT)
177
+
178
+ # Use instructor's structured generation with function calling
179
+ response = await patched_client.chat.completions.create(
152
180
  model=self.parse_model,
153
181
  messages=messages,
154
- response_format=model_class,
182
+ response_model=model_class,
183
+ max_retries=2 # Automatically retry on validation errors
155
184
  )
156
- return completion.choices[0].message.parsed
185
+ return response
157
186
  except Exception as e:
158
- print(f"Error with beta.parse method: {e}")
187
+ print(
188
+ f"Error with instructor parsing (TOOLS_STRICT mode): {e}")
189
+
190
+ try:
191
+ # First fallback: Try regular JSON mode
192
+ patched_client = instructor.from_openai(
193
+ self.client, mode=Mode.JSON)
194
+ response = await patched_client.chat.completions.create(
195
+ model=self.parse_model,
196
+ messages=messages,
197
+ response_model=model_class,
198
+ max_retries=1
199
+ )
200
+ return response
201
+ except Exception as json_error:
202
+ print(f"JSON mode fallback also failed: {json_error}")
203
+
204
+ try:
205
+ # Final fallback: Manual extraction with a detailed prompt
206
+ fallback_system_prompt = f"""
207
+ {system_prompt}
208
+
209
+ You must respond with valid JSON that can be parsed as the following Pydantic model:
210
+ {model_class.model_json_schema()}
211
+
212
+ Ensure the response contains ONLY the JSON object and nothing else.
213
+ """
214
+
215
+ # Regular completion without instructor
216
+ completion = await self.client.chat.completions.create(
217
+ model=self.parse_model,
218
+ messages=[
219
+ {"role": "system", "content": fallback_system_prompt},
220
+ {"role": "user", "content": prompt}
221
+ ],
222
+ response_format={"type": "json_object"}
223
+ )
224
+
225
+ # Extract and parse the JSON response
226
+ json_str = completion.choices[0].message.content
227
+
228
+ # Use Pydantic to parse and validate
229
+ return model_class.model_validate_json(json_str)
230
+
231
+ except Exception as fallback_error:
232
+ print(f"All fallback methods failed: {fallback_error}")
233
+ raise ValueError(
234
+ f"Failed to generate structured output: {e}. All fallbacks failed."
235
+ ) from e
@@ -86,23 +86,43 @@ class SolanaAgentFactory:
86
86
  zep_api_key=config["zep"].get("api_key")
87
87
  )
88
88
 
89
- # Create primary services
90
- agent_service = AgentService(
91
- llm_provider=llm_adapter,
92
- business_mission=business_mission,
93
- config=config,
94
- )
89
+ if "gemini" in config and "api_key" in config["gemini"]:
90
+ # Create primary services
91
+ agent_service = AgentService(
92
+ llm_provider=llm_adapter,
93
+ business_mission=business_mission,
94
+ config=config,
95
+ api_key=config["gemini"]["api_key"],
96
+ base_url="https://generativelanguage.googleapis.com/v1beta/openai/",
97
+ model="gemini-2.0-flash",
98
+ )
99
+
100
+ # Create routing service
101
+ routing_service = RoutingService(
102
+ llm_provider=llm_adapter,
103
+ agent_service=agent_service,
104
+ api_key=config["gemini"]["api_key"],
105
+ base_url="https://generativelanguage.googleapis.com/v1beta/openai/",
106
+ model="gemini-2.0-flash",
107
+ )
108
+ else:
109
+ # Create primary services
110
+ agent_service = AgentService(
111
+ llm_provider=llm_adapter,
112
+ business_mission=business_mission,
113
+ config=config,
114
+ )
115
+
116
+ # Create routing service
117
+ routing_service = RoutingService(
118
+ llm_provider=llm_adapter,
119
+ agent_service=agent_service,
120
+ )
95
121
 
96
122
  # Debug the agent service tool registry
97
123
  print(
98
124
  f"Agent service tools after initialization: {agent_service.tool_registry.list_all_tools()}")
99
125
 
100
- # Create routing service
101
- routing_service = RoutingService(
102
- llm_provider=llm_adapter,
103
- agent_service=agent_service,
104
- )
105
-
106
126
  # Initialize plugin system
107
127
  agent_service.plugin_manager = PluginManager(
108
128
  config=config,
@@ -15,13 +15,21 @@ class LLMProvider(ABC):
15
15
  self,
16
16
  prompt: str,
17
17
  system_prompt: str = "",
18
+ api_key: Optional[str] = None,
19
+ base_url: Optional[str] = None,
20
+ model: Optional[str] = None,
18
21
  ) -> AsyncGenerator[str, None]:
19
22
  """Generate text from the language model."""
20
23
  pass
21
24
 
22
25
  @abstractmethod
23
26
  async def parse_structured_output(
24
- self, prompt: str, system_prompt: str, model_class: Type[T],
27
+ self, prompt: str,
28
+ system_prompt: str,
29
+ model_class: Type[T],
30
+ api_key: Optional[str] = None,
31
+ base_url: Optional[str] = None,
32
+ model: Optional[str] = None
25
33
  ) -> T:
26
34
  """Generate structured output using a specific model class."""
27
35
  pass
@@ -49,13 +57,3 @@ class LLMProvider(ABC):
49
57
  ) -> AsyncGenerator[str, None]:
50
58
  """Transcribe audio from the language model."""
51
59
  pass
52
-
53
- @abstractmethod
54
- async def realtime_audio_transcription(
55
- self,
56
- audio_generator: AsyncGenerator[bytes, None],
57
- transcription_config: Optional[Dict[str, Any]] = None,
58
- on_event: Optional[Callable[[Dict[str, Any]], Any]] = None,
59
- ) -> AsyncGenerator[str, None]:
60
- """Stream real-time audio transcription from the language model."""
61
- pass
@@ -30,9 +30,6 @@ class AgentService(ABC):
30
30
  audio_instructions: str = "You speak in a friendly and helpful manner.",
31
31
  audio_output_format: Literal['mp3', 'opus',
32
32
  'aac', 'flac', 'wav', 'pcm'] = "aac",
33
- audio_input_format: Literal[
34
- "flac", "mp3", "mp4", "mpeg", "mpga", "m4a", "ogg", "wav", "webm"
35
- ] = "mp4",
36
33
  prompt: Optional[str] = None,
37
34
  ) -> AsyncGenerator[Union[str, bytes], None]:
38
35
  """Generate a response from an agent."""
@@ -26,6 +26,9 @@ class AgentService(AgentServiceInterface):
26
26
  llm_provider: LLMProvider,
27
27
  business_mission: Optional[BusinessMission] = None,
28
28
  config: Optional[Dict[str, Any]] = None,
29
+ api_key: Optional[str] = None,
30
+ base_url: Optional[str] = None,
31
+ model: Optional[str] = None,
29
32
  ):
30
33
  """Initialize the agent service.
31
34
 
@@ -40,6 +43,9 @@ class AgentService(AgentServiceInterface):
40
43
  self.last_text_response = ""
41
44
  self.tool_registry = ToolRegistry(config=self.config)
42
45
  self.agents: List[AIAgent] = []
46
+ self.api_key = api_key
47
+ self.base_url = base_url
48
+ self.model = model
43
49
 
44
50
  self.plugin_manager = PluginManager(
45
51
  config=self.config,
@@ -173,9 +179,6 @@ class AgentService(AgentServiceInterface):
173
179
  audio_instructions: str = "You speak in a friendly and helpful manner.",
174
180
  audio_output_format: Literal['mp3', 'opus',
175
181
  'aac', 'flac', 'wav', 'pcm'] = "aac",
176
- audio_input_format: Literal[
177
- "flac", "mp3", "mp4", "mpeg", "mpga", "m4a", "ogg", "wav", "webm"
178
- ] = "mp4",
179
182
  prompt: Optional[str] = None,
180
183
  ) -> AsyncGenerator[Union[str, bytes], None]: # pragma: no cover
181
184
  """Generate a response with support for text/audio input/output."""
@@ -191,14 +194,6 @@ class AgentService(AgentServiceInterface):
191
194
  return
192
195
 
193
196
  try:
194
- # Handle audio input if provided - KEEP REAL-TIME AUDIO TRANSCRIPTION
195
- query_text = ""
196
- if not isinstance(query, str):
197
- async for transcript in self.llm_provider.transcribe_audio(query, input_format=audio_input_format):
198
- query_text += transcript
199
- else:
200
- query_text = query
201
-
202
197
  # Get system prompt
203
198
  system_prompt = self.get_agent_system_prompt(agent_name)
204
199
 
@@ -234,10 +229,13 @@ class AgentService(AgentServiceInterface):
234
229
 
235
230
  # Generate and stream response (ALWAYS use non-realtime for text generation)
236
231
  print(
237
- f"Generating response with {len(query_text)} characters of query text")
232
+ f"Generating response with {len(query)} characters of query text")
238
233
  async for chunk in self.llm_provider.generate_text(
239
- prompt=query_text,
234
+ prompt=query,
240
235
  system_prompt=tool_calling_system_prompt,
236
+ api_key=self.api_key,
237
+ base_url=self.base_url,
238
+ model=self.model,
241
239
  ):
242
240
  # If we have pending text from the previous chunk, combine it with this chunk
243
241
  if pending_chunk:
@@ -288,7 +286,7 @@ class AgentService(AgentServiceInterface):
288
286
 
289
287
  # Create new prompt with search/tool results
290
288
  # Using "Search Result" instead of "TOOL RESPONSE" to avoid model repeating "TOOL"
291
- user_prompt = f"{query_text}\n\nSearch Result: {response_text}"
289
+ user_prompt = f"{query}\n\nSearch Result: {response_text}"
292
290
  tool_system_prompt = system_prompt + \
293
291
  "\n DO NOT use the tool calling format again."
294
292
 
@@ -299,6 +297,9 @@ class AgentService(AgentServiceInterface):
299
297
  async for processed_chunk in self.llm_provider.generate_text(
300
298
  prompt=user_prompt,
301
299
  system_prompt=tool_system_prompt,
300
+ api_key=self.api_key,
301
+ base_url=self.base_url,
302
+ model=self.model,
302
303
  ):
303
304
  complete_text_response += processed_chunk
304
305
  yield processed_chunk
@@ -112,11 +112,10 @@ class QueryService(QueryServiceInterface):
112
112
  async for audio_chunk in self.agent_service.generate_response(
113
113
  agent_name=agent_name,
114
114
  user_id=user_id,
115
- query=query,
115
+ query=user_text,
116
116
  memory_context=memory_context,
117
117
  output_format="audio",
118
118
  audio_voice=audio_voice,
119
- audio_input_format=audio_input_format,
120
119
  audio_output_format=audio_output_format,
121
120
  audio_instructions=audio_instructions,
122
121
  prompt=prompt,
@@ -18,6 +18,9 @@ class RoutingService(RoutingServiceInterface):
18
18
  self,
19
19
  llm_provider: LLMProvider,
20
20
  agent_service: AgentService,
21
+ api_key: Optional[str] = None,
22
+ base_url: Optional[str] = None,
23
+ model: Optional[str] = None,
21
24
  ):
22
25
  """Initialize the routing service.
23
26
 
@@ -27,6 +30,9 @@ class RoutingService(RoutingServiceInterface):
27
30
  """
28
31
  self.llm_provider = llm_provider
29
32
  self.agent_service = agent_service
33
+ self.api_key = api_key
34
+ self.base_url = base_url
35
+ self.model = model
30
36
 
31
37
  async def _analyze_query(self, query: str) -> Dict[str, Any]:
32
38
  """Analyze a query to determine routing information.
@@ -75,6 +81,9 @@ class RoutingService(RoutingServiceInterface):
75
81
  prompt=prompt,
76
82
  system_prompt="Match user queries to the most appropriate agent based on specializations.",
77
83
  model_class=QueryAnalysis,
84
+ api_key=self.api_key,
85
+ base_url=self.base_url,
86
+ model=self.model,
78
87
  )
79
88
 
80
89
  return {
File without changes