solana-agent 21.1.0__py3-none-any.whl → 22.0.1__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.
@@ -20,7 +20,7 @@ class OpenAIAdapter(LLMProvider):
20
20
  self.client = OpenAI(api_key=api_key)
21
21
  self.parse_model = "gpt-4o-mini"
22
22
  self.text_model = "gpt-4o-mini"
23
- self.search_model = "gpt-4o-mini-search-preview"
23
+ self.internet_search_model = "gpt-4o-mini-search-preview"
24
24
  self.transcription_model = "gpt-4o-mini-transcribe"
25
25
  self.tts_model = "tts-1"
26
26
 
@@ -106,7 +106,7 @@ class OpenAIAdapter(LLMProvider):
106
106
  self,
107
107
  prompt: str,
108
108
  system_prompt: str = "",
109
- search: bool = False,
109
+ internet_search: bool = False,
110
110
  ) -> AsyncGenerator[str, None]: # pragma: no cover
111
111
  """Generate text from OpenAI models."""
112
112
  messages = []
@@ -117,8 +117,8 @@ class OpenAIAdapter(LLMProvider):
117
117
  messages.append({"role": "user", "content": prompt})
118
118
 
119
119
  model = self.text_model
120
- if search:
121
- model = self.search_model
120
+ if internet_search:
121
+ model = self.internet_search_model
122
122
 
123
123
  # Prepare request parameters
124
124
  request_params = {
@@ -56,7 +56,7 @@ class SolanaAgent(SolanaAgentInterface):
56
56
  "flac", "mp3", "mp4", "mpeg", "mpga", "m4a", "ogg", "wav", "webm"
57
57
  ] = "mp4",
58
58
  router: Optional[RoutingInterface] = None,
59
- use_openai_search: bool = True,
59
+ internet_search: bool = True,
60
60
  ) -> AsyncGenerator[Union[str, bytes], None]: # pragma: no cover
61
61
  """Process a user message and return the response stream.
62
62
 
@@ -70,7 +70,7 @@ class SolanaAgent(SolanaAgentInterface):
70
70
  audio_output_format: Audio output format
71
71
  audio_input_format: Audio input format
72
72
  router: Optional routing service for processing
73
- use_openai_search: Flag to use OpenAI search
73
+ internet_search: Flag to use OpenAI Internet search
74
74
 
75
75
  Returns:
76
76
  Async generator yielding response chunks (text strings or audio bytes)
@@ -85,7 +85,7 @@ class SolanaAgent(SolanaAgentInterface):
85
85
  audio_input_format=audio_input_format,
86
86
  prompt=prompt,
87
87
  router=router,
88
- use_openai_search=use_openai_search,
88
+ internet_search=internet_search,
89
89
  ):
90
90
  yield chunk
91
91
 
@@ -74,17 +74,16 @@ class SolanaAgentFactory:
74
74
  if "api_key" not in config["zep"]:
75
75
  raise ValueError("Zep API key is required.")
76
76
  memory_provider = MemoryRepository(
77
- db_adapter, config["zep"].get("api_key"), config["zep"].get("base_url"))
77
+ mongo_adapter=db_adapter, zep_api_key=config["zep"].get("api_key"))
78
78
 
79
79
  if "mongo" in config and not "zep" in config:
80
- memory_provider = MemoryRepository(db_adapter)
80
+ memory_provider = MemoryRepository(mongo_adapter=db_adapter)
81
81
 
82
82
  if "zep" in config and not "mongo" in config:
83
83
  if "api_key" not in config["zep"]:
84
84
  raise ValueError("Zep API key is required.")
85
85
  memory_provider = MemoryRepository(
86
- zep_api_key=config["zep"].get("api_key"),
87
- zep_base_url=config["zep"].get("base_url")
86
+ zep_api_key=config["zep"].get("api_key")
88
87
  )
89
88
 
90
89
  # Create primary services
@@ -24,7 +24,7 @@ class SolanaAgent(ABC):
24
24
  "flac", "mp3", "mp4", "mpeg", "mpga", "m4a", "ogg", "wav", "webm"
25
25
  ] = "mp4",
26
26
  router: Optional[RoutingInterface] = None,
27
- use_openai_search: bool = True,
27
+ internet_search: bool = True,
28
28
  ) -> AsyncGenerator[Union[str, bytes], None]:
29
29
  """Process a user message and return the response stream."""
30
30
  pass
@@ -15,7 +15,7 @@ class LLMProvider(ABC):
15
15
  self,
16
16
  prompt: str,
17
17
  system_prompt: str = "",
18
- search: bool = False,
18
+ internet_search: bool = False,
19
19
  ) -> AsyncGenerator[str, None]:
20
20
  """Generate text from the language model."""
21
21
  pass
@@ -34,7 +34,7 @@ class AgentService(ABC):
34
34
  "flac", "mp3", "mp4", "mpeg", "mpga", "m4a", "ogg", "wav", "webm"
35
35
  ] = "mp4",
36
36
  prompt: Optional[str] = None,
37
- use_openai_search: bool = True,
37
+ internet_search: bool = True,
38
38
  ) -> AsyncGenerator[Union[str, bytes], None]:
39
39
  """Generate a response from an agent."""
40
40
  pass
@@ -20,7 +20,7 @@ class QueryService(ABC):
20
20
  "flac", "mp3", "mp4", "mpeg", "mpga", "m4a", "ogg", "wav", "webm"
21
21
  ] = "mp4",
22
22
  prompt: Optional[str] = None,
23
- use_openai_search: bool = True,
23
+ internet_search: bool = True,
24
24
  ) -> AsyncGenerator[Union[str, bytes], None]:
25
25
  """Process the user request and generate a response."""
26
26
  pass
@@ -1,7 +1,7 @@
1
+ from copy import deepcopy
1
2
  from typing import List, Dict, Any, Optional, Tuple
2
3
  from datetime import datetime, timezone
3
4
  from zep_cloud.client import AsyncZep as AsyncZepCloud
4
- from zep_python.client import AsyncZep
5
5
  from zep_cloud.types import Message
6
6
  from solana_agent.interfaces.providers.memory import MemoryProvider
7
7
  from solana_agent.adapters.mongodb_adapter import MongoDBAdapter
@@ -14,7 +14,6 @@ class MemoryRepository(MemoryProvider):
14
14
  self,
15
15
  mongo_adapter: Optional[MongoDBAdapter] = None,
16
16
  zep_api_key: Optional[str] = None,
17
- zep_base_url: Optional[str] = None
18
17
  ):
19
18
  """Initialize the combined memory provider."""
20
19
  if not mongo_adapter:
@@ -33,13 +32,10 @@ class MemoryRepository(MemoryProvider):
33
32
  except Exception as e:
34
33
  print(f"Error initializing MongoDB: {e}")
35
34
 
35
+ self.zep = None
36
36
  # Initialize Zep
37
- if zep_api_key and not zep_base_url:
37
+ if zep_api_key:
38
38
  self.zep = AsyncZepCloud(api_key=zep_api_key)
39
- elif zep_api_key and zep_base_url:
40
- self.zep = AsyncZep(api_key=zep_api_key, base_url=zep_base_url)
41
- else:
42
- self.zep = None
43
39
 
44
40
  async def store(self, user_id: str, messages: List[Dict[str, Any]]) -> None:
45
41
  """Store messages in both Zep and MongoDB."""
@@ -99,9 +95,10 @@ class MemoryRepository(MemoryProvider):
99
95
  zep_messages = []
100
96
  for msg in messages:
101
97
  if "role" in msg and "content" in msg:
98
+ content = self._truncate(deepcopy(msg["content"]))
102
99
  zep_msg = Message(
103
100
  role=msg["role"],
104
- content=msg["content"],
101
+ content=content,
105
102
  role_type=msg["role"],
106
103
  )
107
104
  zep_messages.append(zep_msg)
@@ -196,4 +193,4 @@ class MemoryRepository(MemoryProvider):
196
193
  return text[:last_period + 1]
197
194
 
198
195
  # If no period found, truncate at limit and add ellipsis
199
- return text[:limit] + "..."
196
+ return text[:limit-3] + "..."
@@ -176,31 +176,15 @@ class AgentService(AgentServiceInterface):
176
176
  "flac", "mp3", "mp4", "mpeg", "mpga", "m4a", "ogg", "wav", "webm"
177
177
  ] = "mp4",
178
178
  prompt: Optional[str] = None,
179
- use_openai_search: bool = True,
179
+ internet_search: bool = True,
180
180
  ) -> AsyncGenerator[Union[str, bytes], None]: # pragma: no cover
181
- """Generate a response with support for text/audio input/output.
182
-
183
- Args:
184
- agent_name: Agent name
185
- user_id: User ID
186
- query: Text query or audio bytes
187
- memory_context: Optional conversation context
188
- output_format: Response format ("text" or "audio")
189
- audio_voice: Voice to use for audio output
190
- audio_instructions: Optional instructions for audio synthesis
191
- audio_output_format: Audio output format
192
- audio_input_format: Audio input format
193
- prompt: Optional prompt for the agent
194
- use_openai_search: Flag to use OpenAI search
195
-
196
- Yields:
197
- Text chunks or audio bytes depending on output_format
198
- """
181
+ """Generate a response with support for text/audio input/output."""
199
182
  agent = next((a for a in self.agents if a.name == agent_name), None)
200
183
  if not agent:
201
184
  error_msg = f"Agent '{agent_name}' not found."
202
185
  if output_format == "audio":
203
- async for chunk in self.llm_provider.tts(error_msg, instructions=audio_instructions, response_format=audio_output_format, voice=audio_voice):
186
+ async for chunk in self.llm_provider.tts(error_msg, instructions=audio_instructions,
187
+ response_format=audio_output_format, voice=audio_voice):
204
188
  yield chunk
205
189
  else:
206
190
  yield error_msg
@@ -229,20 +213,24 @@ class AgentService(AgentServiceInterface):
229
213
  if prompt:
230
214
  system_prompt += f"\n\nADDITIONAL PROMPT: {prompt}"
231
215
 
232
- # Keep track of the complete text response
216
+ # Variables for tracking the response
233
217
  complete_text_response = ""
218
+
219
+ # For audio output, we'll collect everything first
220
+ full_response_buffer = ""
221
+
222
+ # Variables for handling JSON processing
234
223
  json_buffer = ""
235
224
  is_json = False
236
- text_buffer = ""
237
225
 
238
226
  # Generate and stream response
239
227
  async for chunk in self.llm_provider.generate_text(
240
228
  prompt=query_text,
241
229
  system_prompt=system_prompt,
242
- search=use_openai_search,
230
+ internet_search=internet_search,
243
231
  ):
244
- # Check for JSON start
245
- if chunk.strip().startswith("{") and not is_json:
232
+ # Check if the chunk is JSON or a tool call
233
+ if (chunk.strip().startswith("{") or "{\"tool_call\":" in chunk) and not is_json:
246
234
  is_json = True
247
235
  json_buffer = chunk
248
236
  continue
@@ -256,107 +244,105 @@ class AgentService(AgentServiceInterface):
256
244
 
257
245
  # Valid JSON found, handle it
258
246
  if "tool_call" in data:
259
- # Process tool call with existing method
260
247
  response_text = await self._handle_tool_call(
261
248
  agent_name=agent_name,
262
249
  json_chunk=json_buffer
263
250
  )
264
251
 
265
- system_prompt = system_prompt + \
252
+ # Update system prompt to prevent further tool calls
253
+ tool_system_prompt = system_prompt + \
266
254
  "\n DO NOT make any tool calls or return JSON."
267
255
 
256
+ # Create prompt with tool response
268
257
  user_prompt = f"\n USER QUERY: {query_text} \n"
269
258
  user_prompt += f"\n TOOL RESPONSE: {response_text} \n"
270
259
 
271
- # Collect all processed text first
272
- processed_text = ""
273
- async for processed_chunk in self.llm_provider.generate_text(
274
- prompt=user_prompt,
275
- system_prompt=system_prompt,
276
- ):
277
- processed_text += processed_chunk
278
- # For text output, yield chunks as they come
279
- if output_format == "text":
260
+ # For text output, process chunks directly
261
+ if output_format == "text":
262
+ # Stream text response for text output
263
+ async for processed_chunk in self.llm_provider.generate_text(
264
+ prompt=user_prompt,
265
+ system_prompt=tool_system_prompt,
266
+ ):
267
+ complete_text_response += processed_chunk
280
268
  yield processed_chunk
281
-
282
- # Add to complete response
283
- complete_text_response += processed_text
284
-
285
- # For audio output, process the complete text
286
- if output_format == "audio":
287
- async for audio_chunk in self.llm_provider.tts(
288
- text=processed_text,
289
- voice=audio_voice,
290
- response_format=audio_output_format
269
+ else:
270
+ # For audio output, collect the full tool response first
271
+ tool_response = ""
272
+ async for processed_chunk in self.llm_provider.generate_text(
273
+ prompt=user_prompt,
274
+ system_prompt=tool_system_prompt,
291
275
  ):
292
- yield audio_chunk
276
+ tool_response += processed_chunk
277
+
278
+ # Add to our complete text record and full audio buffer
279
+ tool_response = self._remove_markdown(
280
+ tool_response)
281
+ complete_text_response += tool_response
282
+ full_response_buffer += tool_response
293
283
  else:
294
284
  # For non-tool JSON, still capture the text
295
285
  complete_text_response += json_buffer
296
286
 
297
- if output_format == "audio":
298
- async for audio_chunk in self.llm_provider.tts(
299
- text=json_buffer,
300
- voice=audio_voice,
301
- response_format=audio_output_format
302
- ):
303
- yield audio_chunk
304
- else:
287
+ if output_format == "text":
305
288
  yield json_buffer
289
+ else:
290
+ # Add to full response buffer for audio
291
+ full_response_buffer += json_buffer
306
292
 
307
293
  # Reset JSON handling
308
294
  is_json = False
309
295
  json_buffer = ""
310
296
 
311
297
  except json.JSONDecodeError:
298
+ # JSON not complete yet, continue collecting
312
299
  pass
313
300
  else:
314
- # For regular text, always add to the complete response
301
+ # For regular text
315
302
  complete_text_response += chunk
316
303
 
317
- # Handle audio buffering or direct text output
318
- if output_format == "audio":
319
- text_buffer += chunk
320
- if any(punct in chunk for punct in ".!?"):
321
- async for audio_chunk in self.llm_provider.tts(
322
- text=text_buffer,
323
- voice=audio_voice,
324
- response_format=audio_output_format
325
- ):
326
- yield audio_chunk
327
- text_buffer = ""
328
- else:
304
+ if output_format == "text":
305
+ # For text output, yield directly
329
306
  yield chunk
330
-
331
- # Handle any remaining text or incomplete JSON
332
- remaining_text = ""
333
- if text_buffer:
334
- remaining_text += text_buffer
335
- if is_json and json_buffer:
336
- remaining_text += json_buffer
337
-
338
- if remaining_text:
339
- # Add remaining text to complete response
340
- complete_text_response += remaining_text
341
-
342
- if output_format == "audio":
343
- async for audio_chunk in self.llm_provider.tts(
344
- text=remaining_text,
345
- voice=audio_voice,
346
- response_format=audio_output_format
347
- ):
348
- yield audio_chunk
307
+ else:
308
+ # For audio output, add to the full response buffer
309
+ full_response_buffer += chunk
310
+
311
+ # Handle any leftover JSON buffer
312
+ if json_buffer:
313
+ complete_text_response += json_buffer
314
+ if output_format == "text":
315
+ yield json_buffer
349
316
  else:
350
- yield remaining_text
351
-
352
- # Store the complete text response for the caller to access
353
- # This needs to be done in the query service using the self.last_text_response
317
+ full_response_buffer += json_buffer
318
+
319
+ # For audio output, now process the complete response
320
+ if output_format == "audio" and full_response_buffer:
321
+ # Clean markdown before TTS
322
+ full_response_buffer = self._remove_markdown(
323
+ full_response_buffer)
324
+
325
+ # Process the entire response with TTS
326
+ async for audio_chunk in self.llm_provider.tts(
327
+ text=full_response_buffer,
328
+ voice=audio_voice,
329
+ response_format=audio_output_format,
330
+ instructions=audio_instructions
331
+ ):
332
+ yield audio_chunk
333
+
334
+ # Store the complete text response
354
335
  self.last_text_response = complete_text_response
355
336
 
356
337
  except Exception as e:
357
338
  error_msg = f"I apologize, but I encountered an error: {str(e)}"
358
339
  if output_format == "audio":
359
- async for chunk in self.llm_provider.tts(error_msg, voice=audio_voice, response_format=audio_output_format):
340
+ async for chunk in self.llm_provider.tts(
341
+ error_msg,
342
+ voice=audio_voice,
343
+ response_format=audio_output_format,
344
+ instructions=audio_instructions
345
+ ):
360
346
  yield chunk
361
347
  else:
362
348
  yield error_msg
@@ -379,15 +365,31 @@ class AgentService(AgentServiceInterface):
379
365
  parameters = tool_data.get("parameters", {})
380
366
 
381
367
  if tool_name:
382
- result = await self.execute_tool(
383
- agent_name, tool_name, parameters)
368
+ # Execute the tool and get the result
369
+ result = await self.execute_tool(agent_name, tool_name, parameters)
370
+
384
371
  if result.get("status") == "success":
385
- return result.get("result", "")
372
+ tool_result = result.get("result", "")
373
+ return tool_result
386
374
  else:
387
- return f"I apologize, but I encountered an issue: {result.get('message', 'Unknown error')}"
388
- return json_chunk
389
- except json.JSONDecodeError:
375
+ error_message = f"I apologize, but I encountered an issue with the {tool_name} tool: {result.get('message', 'Unknown error')}"
376
+ print(f"Tool error: {error_message}")
377
+ return error_message
378
+ else:
379
+ return "Tool name was not provided in the tool call."
380
+ else:
381
+ print(f"JSON received but no tool_call found: {json_chunk}")
382
+
383
+ # If we get here, it wasn't properly handled as a tool
384
+ return f"The following request was not processed as a valid tool call:\n{json_chunk}"
385
+ except json.JSONDecodeError as e:
386
+ print(f"JSON decode error in tool call: {e}")
390
387
  return json_chunk
388
+ except Exception as e:
389
+ print(f"Unexpected error in tool call handling: {str(e)}")
390
+ import traceback
391
+ print(traceback.format_exc())
392
+ return f"Error processing tool call: {str(e)}"
391
393
 
392
394
  def _get_tool_usage_prompt(self, agent_name: str) -> str:
393
395
  """Generate JSON-based instructions for tool usage."""
@@ -424,3 +426,47 @@ class AgentService(AgentServiceInterface):
424
426
  - No explanation text before or after
425
427
  - Use exact tool names as shown in AVAILABLE TOOLS
426
428
  """
429
+
430
+ def _remove_markdown(self, text: str) -> str:
431
+ """Remove Markdown formatting and links from text.
432
+
433
+ Args:
434
+ text: Input text with potential Markdown formatting
435
+
436
+ Returns:
437
+ Clean text without Markdown formatting
438
+ """
439
+ import re
440
+
441
+ if not text:
442
+ return ""
443
+
444
+ # Remove Markdown links - [text](url) -> text
445
+ text = re.sub(r'\[([^\]]+)\]\([^\)]+\)', r'\1', text)
446
+
447
+ # Remove inline code with backticks
448
+ text = re.sub(r'`([^`]+)`', r'\1', text)
449
+
450
+ # Remove bold formatting - **text** or __text__ -> text
451
+ text = re.sub(r'(\*\*|__)(.*?)\1', r'\2', text)
452
+
453
+ # Remove italic formatting - *text* or _text_ -> text
454
+ text = re.sub(r'(\*|_)(.*?)\1', r'\2', text)
455
+
456
+ # Remove headers - ## Header -> Header
457
+ text = re.sub(r'^\s*#+\s*(.*?)$', r'\1', text, flags=re.MULTILINE)
458
+
459
+ # Remove blockquotes - > Text -> Text
460
+ text = re.sub(r'^\s*>\s*(.*?)$', r'\1', text, flags=re.MULTILINE)
461
+
462
+ # Remove horizontal rules (---, ***, ___)
463
+ text = re.sub(r'^\s*[-*_]{3,}\s*$', '', text, flags=re.MULTILINE)
464
+
465
+ # Remove list markers - * Item or - Item or 1. Item -> Item
466
+ text = re.sub(r'^\s*[-*+]\s+(.*?)$', r'\1', text, flags=re.MULTILINE)
467
+ text = re.sub(r'^\s*\d+\.\s+(.*?)$', r'\1', text, flags=re.MULTILINE)
468
+
469
+ # Remove multiple consecutive newlines (keep just one)
470
+ text = re.sub(r'\n{3,}', '\n\n', text)
471
+
472
+ return text.strip()
@@ -49,7 +49,7 @@ class QueryService(QueryServiceInterface):
49
49
  ] = "mp4",
50
50
  prompt: Optional[str] = None,
51
51
  router: Optional[RoutingServiceInterface] = None,
52
- use_openai_search: bool = True,
52
+ internet_search: bool = True,
53
53
  ) -> AsyncGenerator[Union[str, bytes], None]: # pragma: no cover
54
54
  """Process the user request with appropriate agent.
55
55
 
@@ -63,7 +63,7 @@ class QueryService(QueryServiceInterface):
63
63
  audio_input_format: Audio input format
64
64
  prompt: Optional prompt for the agent
65
65
  router: Optional routing service for processing
66
- use_openai_search: Flag to use OpenAI search
66
+ internet_search: Flag to use OpenAI Internet search
67
67
 
68
68
  Yields:
69
69
  Response chunks (text strings or audio bytes)
@@ -121,7 +121,7 @@ class QueryService(QueryServiceInterface):
121
121
  audio_output_format=audio_output_format,
122
122
  audio_instructions=audio_instructions,
123
123
  prompt=prompt,
124
- use_openai_search=use_openai_search,
124
+ internet_search=internet_search,
125
125
  ):
126
126
  yield audio_chunk
127
127
 
@@ -140,7 +140,7 @@ class QueryService(QueryServiceInterface):
140
140
  memory_context=memory_context,
141
141
  output_format="text",
142
142
  prompt=prompt,
143
- use_openai_search=use_openai_search,
143
+ internet_search=internet_search,
144
144
  ):
145
145
  yield chunk
146
146
  full_text_response += chunk
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: solana-agent
3
- Version: 21.1.0
3
+ Version: 22.0.1
4
4
  Summary: Agentic IQ
5
5
  License: MIT
6
6
  Keywords: ai,openai,ai agents,agi
@@ -18,7 +18,6 @@ Requires-Dist: openai (>=1.70.0,<2.0.0)
18
18
  Requires-Dist: pydantic (>=2.11.2,<3.0.0)
19
19
  Requires-Dist: pymongo (>=4.11.3,<5.0.0)
20
20
  Requires-Dist: zep-cloud (>=2.9.0,<3.0.0)
21
- Requires-Dist: zep-python (>=2.0.2,<3.0.0)
22
21
  Project-URL: Documentation, https://docs.solana-agent.com
23
22
  Project-URL: Repository, https://github.com/truemagic-coder/solana-agent
24
23
  Description-Content-Type: text/markdown
@@ -50,7 +49,6 @@ Build your AI business in three lines of code!
50
49
  * Extensible Tooling
51
50
  * Simple Business Definition
52
51
  * Tested & Secure
53
- * Support for MCP Servers
54
52
  * Built in Python
55
53
  * Deployed by [CometHeart](https://cometheart.com) & [WalletBubbles](https://walletbubbles.com)
56
54
 
@@ -67,14 +65,13 @@ Build your AI business in three lines of code!
67
65
  * Powerful tool integration using standard Python packages and/or inline tools
68
66
  * Assigned tools are utilized by agents automatically and effectively
69
67
  * Simple business definition using JSON
70
- * Ability to access any MCP server via URL
71
68
 
72
69
  ## Stack
73
70
 
74
71
  * [Python](https://python.org) - Programming Language
75
72
  * [OpenAI](https://openai.com) - LLMs
76
73
  * [MongoDB](https://mongodb.com) - Conversational History (optional)
77
- * [Zep](https://getzep.com) - Conversational Memory (optional)
74
+ * [Zep Cloud](https://getzep.com) - Conversational Memory (optional)
78
75
 
79
76
  ## Installation
80
77
 
@@ -240,19 +237,26 @@ config = {
240
237
  ```python
241
238
  config = {
242
239
  "zep": {
243
- "api_key": "your-zep-api-key",
244
- "base_url": "your-zep-base-url", # not applicable if using Zep Cloud
240
+ "api_key": "your-zep-cloud-api-key",
245
241
  },
246
242
  }
247
243
  ```
248
244
 
249
- ## Plugins
245
+ ### Disable Internet Searching
250
246
 
251
- Plugins like Solana Agent Kit (sakit) integrate automatically with Solana Agent.
247
+ ```python
248
+ async for response in solana_agent.process("user123", "Write me a poem.", internet_search=False):
249
+ print(response, end="")
250
+ ```
251
+
252
+ ## Tools
253
+
254
+ Tools can be used from plugins like Solana Agent Kit (sakit) or via custom inline tools. Tools available via plugins integrate automatically with Solana Agent.
255
+
256
+ ### Plugin Usage Example
252
257
 
253
258
  `pip install sakit`
254
259
 
255
- ### MCP
256
260
  ```python
257
261
  from solana_agent import SolanaAgent
258
262
 
@@ -261,20 +265,18 @@ config = {
261
265
  "api_key": "your-openai-api-key",
262
266
  },
263
267
  "tools": {
264
- "mcp": {
265
- "server_urls": [
266
- "http://mcp-server1.com/mcp",
267
- "http://mcp-server2.com/mcp",
268
- "http://mcp-server3.com/mcp"
269
- ]
270
- }
268
+ "search_internet": {
269
+ "api_key": "your-perplexity-key", # Required
270
+ "citations": True, # Optional, defaults to True
271
+ "model": "sonar" # Optional, defaults to "sonar"
272
+ },
271
273
  },
272
274
  "agents": [
273
275
  {
274
276
  "name": "research_specialist",
275
277
  "instructions": "You are an expert researcher who synthesizes complex information clearly.",
276
278
  "specialization": "Research and knowledge synthesis",
277
- "tools": ["mcp"],
279
+ "tools": ["search_internet"],
278
280
  },
279
281
  {
280
282
  "name": "customer_support",
@@ -286,15 +288,11 @@ config = {
286
288
 
287
289
  solana_agent = SolanaAgent(config=config)
288
290
 
289
- async for response in solana_agent.process("user123", "What are the latest AI developments?"):
291
+ async for response in solana_agent.process("user123", "What are the latest AI developments?", internet_search=False):
290
292
  print(response, end="")
291
293
  ```
292
294
 
293
- To create a plugin like Solana Agent Kit - read the [code](https://github.com/truemagic-coder/solana-agent-kit)
294
-
295
- ## Advanced
296
-
297
- ### Custom Inline Tools
295
+ ### Custom Inline Tool Example
298
296
 
299
297
  ```python
300
298
  from solana_agent import SolanaAgent
@@ -374,9 +372,11 @@ async for response in solana_agent.process("user123", "What are the latest AI de
374
372
  print(response, end="")
375
373
  ```
376
374
 
377
- ### Custom Prompt Injection at Runtime
375
+ ## Training your Agents
376
+
377
+ Many use-cases for Solana Agent require training your agents on your company data.
378
378
 
379
- Useful for Knowledge Base answers and FAQs
379
+ This can be accomplished via runtime prompt injection. Integrations that work well with this method are KBs like Pinecone and FAQs.
380
380
 
381
381
  ```python
382
382
  from solana_agent import SolanaAgent
@@ -401,11 +401,13 @@ config = {
401
401
 
402
402
  solana_agent = SolanaAgent(config=config)
403
403
 
404
- async for response in solana_agent.process("user123", "What are the latest AI developments?", "Always end your sentences with eh?"):
404
+ async for response in solana_agent.process("user123", "What are the latest AI developments?", "This is my FAQ"):
405
405
  print(response, end="")
406
406
  ```
407
407
 
408
- ### Custom Routing
408
+ ## Custom Routing
409
+
410
+ In advanced cases like implementing a ticketing system on-top of Solana Agent - you can use your own router.
409
411
 
410
412
  ```python
411
413
  from solana_agent import SolanaAgent
@@ -1,22 +1,22 @@
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=MGyk6OHUfsGBIcUYB2rhqjDGqijr5abD5i80iTzhDcQ,5593
3
+ solana_agent/adapters/llm_adapter.py,sha256=PsSkMrsSqZzXAL3NcQ9Zz7UCtyJYU0USvZ7uTD_I8NI,5629
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
- solana_agent/client/solana_agent.py,sha256=WsuzTalJHcD2bn_i8TsxHBoOGuJN31Oc1nNHN2ceiGE,5317
6
+ solana_agent/client/solana_agent.py,sha256=8mu0OdLBQnZXKS2mFrWvvY_4bykzhT73oyIxs9tmJvY,5318
7
7
  solana_agent/domains/__init__.py,sha256=HiC94wVPRy-QDJSSRywCRrhrFfTBeHjfi5z-QfZv46U,168
8
8
  solana_agent/domains/agent.py,sha256=WTo-pEc66V6D_35cpDE-kTsw1SJM-dtylPZ7em5em7Q,2659
9
9
  solana_agent/domains/routing.py,sha256=UDlgTjUoC9xIBVYu_dnf9-KG_bBgdEXAv_UtDOrYo0w,650
10
10
  solana_agent/factories/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
- solana_agent/factories/agent_factory.py,sha256=c5TgZpDZ6m_q8F-Bv36Rl4dXq5yWNvAN2q9Nq_PtqqM,5547
11
+ solana_agent/factories/agent_factory.py,sha256=mJQb1G0-gebizZvSVHm4NAxRMB1kemm2w_BAcYlN15Y,5496
12
12
  solana_agent/interfaces/__init__.py,sha256=IQs1WIM1FeKP1-kY2FEfyhol_dB-I-VAe2rD6jrVF6k,355
13
- solana_agent/interfaces/client/client.py,sha256=g4V3mD-LdVqhdtHcKkoWRXZhpU9FSoz5VEuzD6WWt-U,1699
13
+ solana_agent/interfaces/client/client.py,sha256=RbZWS_YaAyav56cODECMNkYL0MJwA_8bFeUreZp26qo,1697
14
14
  solana_agent/interfaces/plugins/plugins.py,sha256=T8HPBsekmzVwfU_Rizp-vtzAeYkMlKMYD7U9d0Wjq9c,3338
15
15
  solana_agent/interfaces/providers/data_storage.py,sha256=NqGeFvAzhz9rr-liLPRNCGjooB2EIhe-EVsMmX__b0M,1658
16
- solana_agent/interfaces/providers/llm.py,sha256=Tnj6wygZGeJzbPM8HHtu3he2RoWVUAU3RbvNvj6Qopg,1539
16
+ solana_agent/interfaces/providers/llm.py,sha256=Fy7_iTI8sez2NVeE9lDed4W5cXx95HRy0ctEpIqr5N0,1548
17
17
  solana_agent/interfaces/providers/memory.py,sha256=oNOH8WZXVW8assDigIWZAWiwkxbpDiKupxA2RB6tQvQ,1010
18
- solana_agent/interfaces/services/agent.py,sha256=5BvpZypYUfgUDmPUs9jLBNUuVOCety0bMCFO8JS0jPo,2122
19
- solana_agent/interfaces/services/query.py,sha256=2pQKZqPfXujD9QaFUiZyxqQLtTUt32TwVU75BjIiPUk,1349
18
+ solana_agent/interfaces/services/agent.py,sha256=7HOGcvvHTxeK-dMlqw460yqKwGd72JokhDIr3kzroVg,2120
19
+ solana_agent/interfaces/services/query.py,sha256=PGW2w60R615og28Bw6sS1cCcBN_26KkkOsYDYclS1KQ,1347
20
20
  solana_agent/interfaces/services/routing.py,sha256=UzJC-z-Q9puTWPFGEo2_CAhIxuxP5IRnze7S66NSrsI,397
21
21
  solana_agent/plugins/__init__.py,sha256=coZdgJKq1ExOaj6qB810i3rEhbjdVlrkN76ozt_Ojgo,193
22
22
  solana_agent/plugins/manager.py,sha256=Il49hXeqvu0b02pURNNp7mY8kp9_sqpi_vJIWBW5Hc0,5044
@@ -24,12 +24,12 @@ solana_agent/plugins/registry.py,sha256=5S0DlUQKogsg1zLiRUIGMHEmGYHtOovU-S-5W1Mw
24
24
  solana_agent/plugins/tools/__init__.py,sha256=c0z7ij42gs94_VJrcn4Y8gUlTxMhsFNY6ahIsNswdLk,231
25
25
  solana_agent/plugins/tools/auto_tool.py,sha256=DgES_cZ6xKSf_HJpFINpvJxrjVlk5oeqa7pZRBsR9SM,1575
26
26
  solana_agent/repositories/__init__.py,sha256=fP83w83CGzXLnSdq-C5wbw9EhWTYtqE2lQTgp46-X_4,163
27
- solana_agent/repositories/memory.py,sha256=eecl1P0fr_xFSWFKIJg99q90oCS9--ihPrMLH3G2AzM,7136
27
+ solana_agent/repositories/memory.py,sha256=mrpmNSQ0D_eLebNY-cBqtecVVpIGXE7s9jCzOWEAuR4,6984
28
28
  solana_agent/services/__init__.py,sha256=ab_NXJmwYUCmCrCzuTlZ47bJZINW0Y0F5jfQ9OovidU,163
29
- solana_agent/services/agent.py,sha256=viPvUuEJ-W3-iM6gedLPpwGwe7A6wWXkasgYZsb1ppY,16589
30
- solana_agent/services/query.py,sha256=IQinJo1ft5Vb0sKDC6zVsFQEZ_SjdVYz5oUEn5bJ3Zg,11281
29
+ solana_agent/services/agent.py,sha256=uLZvMl8U40H-Lbxsf6PFR4SSfggjByBUoumvB1Afduo,18441
30
+ solana_agent/services/query.py,sha256=gUIMJaTcGUjn7TuwJHE-CHMjQIdcYUNoxqJ3duE-QUg,11278
31
31
  solana_agent/services/routing.py,sha256=PMCSG5m3uLMaHMj3dxNvNfcFZaeaDi7kMr7AEBCzwDE,6499
32
- solana_agent-21.1.0.dist-info/LICENSE,sha256=BnSRc-NSFuyF2s496l_4EyrwAP6YimvxWcjPiJ0J7g4,1057
33
- solana_agent-21.1.0.dist-info/METADATA,sha256=E5XxVoA7SBtM4UzZNc-35_mnT9QwqMfSF9MSFoi5QeQ,14237
34
- solana_agent-21.1.0.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
35
- solana_agent-21.1.0.dist-info/RECORD,,
32
+ solana_agent-22.0.1.dist-info/LICENSE,sha256=BnSRc-NSFuyF2s496l_4EyrwAP6YimvxWcjPiJ0J7g4,1057
33
+ solana_agent-22.0.1.dist-info/METADATA,sha256=iUmnx3K1S69RYYoBNXE8hYBfXugafCX3r_ZMKOtJEjY,14503
34
+ solana_agent-22.0.1.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
35
+ solana_agent-22.0.1.dist-info/RECORD,,