solana-agent 17.0.1__py3-none-any.whl → 17.0.3__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/llm_adapter.py +3 -3
- solana_agent/services/agent.py +91 -20
- solana_agent/services/query.py +33 -27
- {solana_agent-17.0.1.dist-info → solana_agent-17.0.3.dist-info}/METADATA +2 -3
- {solana_agent-17.0.1.dist-info → solana_agent-17.0.3.dist-info}/RECORD +7 -7
- {solana_agent-17.0.1.dist-info → solana_agent-17.0.3.dist-info}/LICENSE +0 -0
- {solana_agent-17.0.1.dist-info → solana_agent-17.0.3.dist-info}/WHEEL +0 -0
@@ -19,7 +19,7 @@ class OpenAIAdapter(LLMProvider):
|
|
19
19
|
def __init__(self, api_key: str):
|
20
20
|
self.client = OpenAI(api_key=api_key)
|
21
21
|
self.parse_model = "gpt-4o-mini"
|
22
|
-
self.
|
22
|
+
self.text_model = "gpt-4o-mini"
|
23
23
|
self.transcription_model = "gpt-4o-mini-transcribe"
|
24
24
|
self.tts_model = "tts-1"
|
25
25
|
|
@@ -106,7 +106,7 @@ class OpenAIAdapter(LLMProvider):
|
|
106
106
|
prompt: str,
|
107
107
|
system_prompt: str = "",
|
108
108
|
) -> AsyncGenerator[str, None]: # pragma: no cover
|
109
|
-
"""Generate text from OpenAI models
|
109
|
+
"""Generate text from OpenAI models."""
|
110
110
|
messages = []
|
111
111
|
|
112
112
|
if system_prompt:
|
@@ -118,7 +118,7 @@ class OpenAIAdapter(LLMProvider):
|
|
118
118
|
request_params = {
|
119
119
|
"messages": messages,
|
120
120
|
"stream": True,
|
121
|
-
"model": self.
|
121
|
+
"model": self.text_model,
|
122
122
|
}
|
123
123
|
try:
|
124
124
|
response = self.client.chat.completions.create(**request_params)
|
solana_agent/services/agent.py
CHANGED
@@ -39,6 +39,7 @@ class AgentService(AgentServiceInterface):
|
|
39
39
|
self.agent_repository = agent_repository
|
40
40
|
self.organization_mission = organization_mission
|
41
41
|
self.config = config or {}
|
42
|
+
self.last_text_response = ""
|
42
43
|
|
43
44
|
# Initialize tool registry with concrete implementation
|
44
45
|
if tool_registry:
|
@@ -247,7 +248,10 @@ class AgentService(AgentServiceInterface):
|
|
247
248
|
if memory_context:
|
248
249
|
system_prompt += f"\n\nMemory Context: {memory_context}"
|
249
250
|
|
250
|
-
#
|
251
|
+
# Keep track of the complete text response
|
252
|
+
complete_text_response = ""
|
253
|
+
json_buffer = ""
|
254
|
+
is_json = False
|
251
255
|
text_buffer = ""
|
252
256
|
|
253
257
|
# Generate and stream response
|
@@ -255,40 +259,107 @@ class AgentService(AgentServiceInterface):
|
|
255
259
|
prompt=query_text,
|
256
260
|
system_prompt=system_prompt,
|
257
261
|
):
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
262
|
+
# Check for JSON start
|
263
|
+
if chunk.strip().startswith("{") and not is_json:
|
264
|
+
is_json = True
|
265
|
+
json_buffer = chunk
|
266
|
+
continue
|
267
|
+
|
268
|
+
# Collect JSON or handle normal text
|
269
|
+
if is_json:
|
270
|
+
json_buffer += chunk
|
271
|
+
try:
|
272
|
+
# Try to parse complete JSON
|
273
|
+
data = json.loads(json_buffer)
|
274
|
+
|
275
|
+
# Valid JSON found, handle it
|
276
|
+
if "tool_call" in data:
|
277
|
+
# Process tool call with existing method
|
278
|
+
response_text = await self._handle_tool_call(
|
279
|
+
agent_name=agent_name,
|
280
|
+
json_chunk=json_buffer
|
281
|
+
)
|
282
|
+
|
283
|
+
# Add to complete text response
|
284
|
+
complete_text_response += response_text
|
285
|
+
|
286
|
+
# Output response based on format
|
287
|
+
if output_format == "audio":
|
288
|
+
async for audio_chunk in self.llm_provider.tts(
|
289
|
+
text=response_text,
|
290
|
+
voice=audio_voice,
|
291
|
+
response_format=audio_output_format
|
292
|
+
):
|
293
|
+
yield audio_chunk
|
294
|
+
else:
|
295
|
+
yield response_text
|
296
|
+
else:
|
297
|
+
# For non-tool JSON, still capture the text
|
298
|
+
complete_text_response += json_buffer
|
299
|
+
|
300
|
+
if output_format == "audio":
|
301
|
+
async for audio_chunk in self.llm_provider.tts(
|
302
|
+
text=json_buffer,
|
303
|
+
voice=audio_voice,
|
304
|
+
response_format=audio_output_format
|
305
|
+
):
|
306
|
+
yield audio_chunk
|
307
|
+
else:
|
308
|
+
yield json_buffer
|
309
|
+
|
310
|
+
# Reset JSON handling
|
311
|
+
is_json = False
|
312
|
+
json_buffer = ""
|
313
|
+
|
314
|
+
except json.JSONDecodeError:
|
315
|
+
pass
|
268
316
|
else:
|
317
|
+
# For regular text, always add to the complete response
|
318
|
+
complete_text_response += chunk
|
319
|
+
|
320
|
+
# Handle audio buffering or direct text output
|
269
321
|
if output_format == "audio":
|
270
|
-
# Buffer text until we have a complete sentence
|
271
322
|
text_buffer += chunk
|
272
323
|
if any(punct in chunk for punct in ".!?"):
|
273
324
|
async for audio_chunk in self.llm_provider.tts(
|
274
|
-
text_buffer,
|
325
|
+
text=text_buffer,
|
326
|
+
voice=audio_voice,
|
327
|
+
response_format=audio_output_format
|
275
328
|
):
|
276
329
|
yield audio_chunk
|
277
330
|
text_buffer = ""
|
278
331
|
else:
|
279
332
|
yield chunk
|
280
333
|
|
281
|
-
# Handle any remaining text
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
334
|
+
# Handle any remaining text or incomplete JSON
|
335
|
+
remaining_text = ""
|
336
|
+
if text_buffer:
|
337
|
+
remaining_text += text_buffer
|
338
|
+
if is_json and json_buffer:
|
339
|
+
remaining_text += json_buffer
|
340
|
+
|
341
|
+
if remaining_text:
|
342
|
+
# Add remaining text to complete response
|
343
|
+
complete_text_response += remaining_text
|
344
|
+
|
345
|
+
if output_format == "audio":
|
346
|
+
async for audio_chunk in self.llm_provider.tts(
|
347
|
+
text=remaining_text,
|
348
|
+
voice=audio_voice,
|
349
|
+
response_format=audio_output_format
|
350
|
+
):
|
351
|
+
yield audio_chunk
|
352
|
+
else:
|
353
|
+
yield remaining_text
|
354
|
+
|
355
|
+
# Store the complete text response for the caller to access
|
356
|
+
# This needs to be done in the query service using the self.last_text_response
|
357
|
+
self.last_text_response = complete_text_response
|
287
358
|
|
288
359
|
except Exception as e:
|
289
360
|
error_msg = f"I apologize, but I encountered an error: {str(e)}"
|
290
361
|
if output_format == "audio":
|
291
|
-
async for chunk in self.llm_provider.tts(error_msg,
|
362
|
+
async for chunk in self.llm_provider.tts(error_msg, voice=audio_voice, response_format=audio_output_format):
|
292
363
|
yield chunk
|
293
364
|
else:
|
294
365
|
yield error_msg
|
solana_agent/services/query.py
CHANGED
@@ -52,11 +52,8 @@ class QueryService(QueryServiceInterface):
|
|
52
52
|
Args:
|
53
53
|
user_id: User ID
|
54
54
|
query: Text query or audio bytes
|
55
|
-
output_format: Response format ("text" or "audio")
|
56
|
-
|
57
|
-
audio_instructions: Optional instructions for audio synthesis
|
58
|
-
audio_output_format: Audio output format
|
59
|
-
audio_input_format: Audio input format
|
55
|
+
output_format: Response format ("text" or "audio")
|
56
|
+
voice: Voice to use for audio output
|
60
57
|
|
61
58
|
Yields:
|
62
59
|
Response chunks (text strings or audio bytes)
|
@@ -74,7 +71,11 @@ class QueryService(QueryServiceInterface):
|
|
74
71
|
if user_text.strip().lower() in ["test", "hello", "hi", "hey", "ping"]:
|
75
72
|
response = "Hello! How can I help you today?"
|
76
73
|
if output_format == "audio":
|
77
|
-
async for chunk in self.agent_service.llm_provider.tts(
|
74
|
+
async for chunk in self.agent_service.llm_provider.tts(
|
75
|
+
text=response,
|
76
|
+
voice=audio_voice,
|
77
|
+
response_format=audio_output_format
|
78
|
+
):
|
78
79
|
yield chunk
|
79
80
|
else:
|
80
81
|
yield response
|
@@ -91,42 +92,47 @@ class QueryService(QueryServiceInterface):
|
|
91
92
|
|
92
93
|
# Route query to appropriate agent
|
93
94
|
agent_name = await self.routing_service.route_query(user_text)
|
95
|
+
print(f"DEBUG: Routed to agent: {agent_name}")
|
94
96
|
|
95
|
-
#
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
yield chunk
|
107
|
-
if output_format == "text":
|
97
|
+
# For text output, we can directly collect and yield chunks
|
98
|
+
if output_format == "text":
|
99
|
+
full_response = ""
|
100
|
+
async for chunk in self.agent_service.generate_response(
|
101
|
+
agent_name=agent_name,
|
102
|
+
user_id=user_id,
|
103
|
+
query=user_text,
|
104
|
+
memory_context=memory_context,
|
105
|
+
output_format="text",
|
106
|
+
):
|
107
|
+
yield chunk
|
108
108
|
full_response += chunk
|
109
109
|
|
110
|
-
# For audio
|
111
|
-
|
112
|
-
# Re-generate response in text format for storage
|
110
|
+
# For audio output, we'll yield audio chunks while collecting text separately
|
111
|
+
else:
|
113
112
|
async for chunk in self.agent_service.generate_response(
|
114
113
|
agent_name=agent_name,
|
115
114
|
user_id=user_id,
|
116
115
|
query=user_text,
|
117
116
|
memory_context=memory_context,
|
118
|
-
output_format="
|
117
|
+
output_format="audio",
|
118
|
+
audio_input_format=audio_input_format,
|
119
|
+
audio_output_format=audio_output_format,
|
120
|
+
audio_voice=audio_voice,
|
119
121
|
):
|
120
|
-
|
122
|
+
yield chunk
|
121
123
|
|
122
|
-
# Store conversation
|
124
|
+
# Store conversation with the full text response
|
123
125
|
if self.memory_provider:
|
124
|
-
await self._store_conversation(user_id, user_text,
|
126
|
+
await self._store_conversation(user_id, user_text, self.agent_service.last_text_response)
|
125
127
|
|
126
128
|
except Exception as e:
|
127
129
|
error_msg = f"I apologize for the technical difficulty. {str(e)}"
|
128
130
|
if output_format == "audio":
|
129
|
-
async for chunk in self.agent_service.llm_provider.tts(
|
131
|
+
async for chunk in self.agent_service.llm_provider.tts(
|
132
|
+
text=error_msg,
|
133
|
+
voice=audio_voice,
|
134
|
+
response_format=audio_output_format
|
135
|
+
):
|
130
136
|
yield chunk
|
131
137
|
else:
|
132
138
|
yield error_msg
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: solana-agent
|
3
|
-
Version: 17.0.
|
3
|
+
Version: 17.0.3
|
4
4
|
Summary: The Future of Work
|
5
5
|
License: MIT
|
6
6
|
Keywords: ai,openai,ai agents,agi
|
@@ -37,7 +37,6 @@ Description-Content-Type: text/markdown
|
|
37
37
|
* Multi-modal input-output streaming with text or audio by AI Agents
|
38
38
|
* Conversational memory per user shared by all AI Agents
|
39
39
|
* Routing based on AI Agent specializations
|
40
|
-
* Built-in Internet Search for all AI Agents
|
41
40
|
* Organizational mission, values, goals, and guidance for all AI Agents
|
42
41
|
* Robust AI Agent tool plugins based on standard python packages
|
43
42
|
|
@@ -110,7 +109,7 @@ async for response in solana_agent.process("user123", "What are the latest AI de
|
|
110
109
|
```
|
111
110
|
|
112
111
|
## Models Used
|
113
|
-
* The model used for AI Agents is `gpt-4o-mini
|
112
|
+
* The model used for AI Agents is `gpt-4o-mini`
|
114
113
|
* The model used for internal structured outputs is `gpt-4o-mini`
|
115
114
|
* The model used for audio_transcription is `gpt-4o-mini-transcribe`
|
116
115
|
* The model used for tts is `tts-1`
|
@@ -1,6 +1,6 @@
|
|
1
1
|
solana_agent/__init__.py,sha256=ceYeUpjIitpln8YK1r0JVJU8mzG6cRPYu-HLny3d-Tw,887
|
2
2
|
solana_agent/adapters/__init__.py,sha256=tiEEuuy0NF3ngc_tGEcRTt71zVI58v3dYY9RvMrF2Cg,204
|
3
|
-
solana_agent/adapters/llm_adapter.py,sha256=
|
3
|
+
solana_agent/adapters/llm_adapter.py,sha256=hi2JYj6CvhNLJxznIv_7Ef7Y0mk7aztHd_OMYe2scMQ,6034
|
4
4
|
solana_agent/adapters/mongodb_adapter.py,sha256=qqEFbY_v1XGyFXBmwd5HSXSSHnA9wWo-Hm1vGEyIG0k,2718
|
5
5
|
solana_agent/client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
6
6
|
solana_agent/client/solana_agent.py,sha256=G0m6GD6N8J3tpjfLoKBMzIR3cZU9qs9cDBfY1mngvzI,4207
|
@@ -28,10 +28,10 @@ solana_agent/repositories/__init__.py,sha256=fP83w83CGzXLnSdq-C5wbw9EhWTYtqE2lQT
|
|
28
28
|
solana_agent/repositories/agent.py,sha256=e1rnsQiigkKwJNLKro86a3b6TBiky3GMfmCRc5b_jPw,3187
|
29
29
|
solana_agent/repositories/memory.py,sha256=GABGwaz00thjviHewLvb18NeKE8dkBROxy_stsiiWrE,4722
|
30
30
|
solana_agent/services/__init__.py,sha256=ab_NXJmwYUCmCrCzuTlZ47bJZINW0Y0F5jfQ9OovidU,163
|
31
|
-
solana_agent/services/agent.py,sha256=
|
32
|
-
solana_agent/services/query.py,sha256=
|
31
|
+
solana_agent/services/agent.py,sha256=j0aI_BGaY5Nhkb9ga0r4BqI3qMKu5TOc4QPQsU3NHyQ,17000
|
32
|
+
solana_agent/services/query.py,sha256=1BAb4UjpydwtJjut8oq724BJohe9ceMigMX-rkI5j7s,10820
|
33
33
|
solana_agent/services/routing.py,sha256=TPJ2Pas4acE93QzMEV6ZP670OtTNrVEPa76fz6urEV4,4996
|
34
|
-
solana_agent-17.0.
|
35
|
-
solana_agent-17.0.
|
36
|
-
solana_agent-17.0.
|
37
|
-
solana_agent-17.0.
|
34
|
+
solana_agent-17.0.3.dist-info/LICENSE,sha256=BnSRc-NSFuyF2s496l_4EyrwAP6YimvxWcjPiJ0J7g4,1057
|
35
|
+
solana_agent-17.0.3.dist-info/METADATA,sha256=kioM7hvaI97S8skANaI4oAcS2gnKgGMvI2jKTTHf5vg,4888
|
36
|
+
solana_agent-17.0.3.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
|
37
|
+
solana_agent-17.0.3.dist-info/RECORD,,
|
File without changes
|
File without changes
|