solana-agent 27.4.3__tar.gz → 28.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 (42) hide show
  1. {solana_agent-27.4.3 → solana_agent-28.0.0}/PKG-INFO +108 -52
  2. {solana_agent-27.4.3 → solana_agent-28.0.0}/README.md +106 -51
  3. {solana_agent-27.4.3 → solana_agent-28.0.0}/pyproject.toml +2 -1
  4. {solana_agent-27.4.3 → solana_agent-28.0.0}/solana_agent/__init__.py +7 -2
  5. {solana_agent-27.4.3 → solana_agent-28.0.0}/solana_agent/adapters/openai_adapter.py +17 -21
  6. {solana_agent-27.4.3 → solana_agent-28.0.0}/solana_agent/factories/agent_factory.py +60 -79
  7. solana_agent-28.0.0/solana_agent/guardrails/pii.py +107 -0
  8. solana_agent-28.0.0/solana_agent/interfaces/guardrails/guardrails.py +26 -0
  9. {solana_agent-27.4.3 → solana_agent-28.0.0}/solana_agent/interfaces/providers/llm.py +1 -1
  10. solana_agent-28.0.0/solana_agent/services/agent.py +952 -0
  11. {solana_agent-27.4.3 → solana_agent-28.0.0}/solana_agent/services/query.py +140 -58
  12. solana_agent-27.4.3/solana_agent/services/agent.py +0 -683
  13. {solana_agent-27.4.3 → solana_agent-28.0.0}/LICENSE +0 -0
  14. {solana_agent-27.4.3 → solana_agent-28.0.0}/solana_agent/adapters/__init__.py +0 -0
  15. {solana_agent-27.4.3 → solana_agent-28.0.0}/solana_agent/adapters/mongodb_adapter.py +0 -0
  16. {solana_agent-27.4.3 → solana_agent-28.0.0}/solana_agent/adapters/pinecone_adapter.py +0 -0
  17. {solana_agent-27.4.3 → solana_agent-28.0.0}/solana_agent/client/__init__.py +0 -0
  18. {solana_agent-27.4.3 → solana_agent-28.0.0}/solana_agent/client/solana_agent.py +0 -0
  19. {solana_agent-27.4.3 → solana_agent-28.0.0}/solana_agent/domains/__init__.py +0 -0
  20. {solana_agent-27.4.3 → solana_agent-28.0.0}/solana_agent/domains/agent.py +0 -0
  21. {solana_agent-27.4.3 → solana_agent-28.0.0}/solana_agent/domains/routing.py +0 -0
  22. {solana_agent-27.4.3 → solana_agent-28.0.0}/solana_agent/factories/__init__.py +0 -0
  23. {solana_agent-27.4.3 → solana_agent-28.0.0}/solana_agent/interfaces/__init__.py +0 -0
  24. {solana_agent-27.4.3 → solana_agent-28.0.0}/solana_agent/interfaces/client/client.py +0 -0
  25. {solana_agent-27.4.3 → solana_agent-28.0.0}/solana_agent/interfaces/plugins/plugins.py +0 -0
  26. {solana_agent-27.4.3 → solana_agent-28.0.0}/solana_agent/interfaces/providers/data_storage.py +0 -0
  27. {solana_agent-27.4.3 → solana_agent-28.0.0}/solana_agent/interfaces/providers/memory.py +0 -0
  28. {solana_agent-27.4.3 → solana_agent-28.0.0}/solana_agent/interfaces/providers/vector_storage.py +0 -0
  29. {solana_agent-27.4.3 → solana_agent-28.0.0}/solana_agent/interfaces/services/agent.py +0 -0
  30. {solana_agent-27.4.3 → solana_agent-28.0.0}/solana_agent/interfaces/services/knowledge_base.py +0 -0
  31. {solana_agent-27.4.3 → solana_agent-28.0.0}/solana_agent/interfaces/services/query.py +0 -0
  32. {solana_agent-27.4.3 → solana_agent-28.0.0}/solana_agent/interfaces/services/routing.py +0 -0
  33. {solana_agent-27.4.3 → solana_agent-28.0.0}/solana_agent/plugins/__init__.py +0 -0
  34. {solana_agent-27.4.3 → solana_agent-28.0.0}/solana_agent/plugins/manager.py +0 -0
  35. {solana_agent-27.4.3 → solana_agent-28.0.0}/solana_agent/plugins/registry.py +0 -0
  36. {solana_agent-27.4.3 → solana_agent-28.0.0}/solana_agent/plugins/tools/__init__.py +0 -0
  37. {solana_agent-27.4.3 → solana_agent-28.0.0}/solana_agent/plugins/tools/auto_tool.py +0 -0
  38. {solana_agent-27.4.3 → solana_agent-28.0.0}/solana_agent/repositories/__init__.py +0 -0
  39. {solana_agent-27.4.3 → solana_agent-28.0.0}/solana_agent/repositories/memory.py +0 -0
  40. {solana_agent-27.4.3 → solana_agent-28.0.0}/solana_agent/services/__init__.py +0 -0
  41. {solana_agent-27.4.3 → solana_agent-28.0.0}/solana_agent/services/knowledge_base.py +0 -0
  42. {solana_agent-27.4.3 → solana_agent-28.0.0}/solana_agent/services/routing.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: solana-agent
3
- Version: 27.4.3
3
+ Version: 28.0.0
4
4
  Summary: AI Agents for Solana
5
5
  License: MIT
6
6
  Keywords: solana,solana ai,solana agent,ai,ai agent,ai agents
@@ -22,6 +22,7 @@ Requires-Dist: pinecone (>=6.0.2,<7.0.0)
22
22
  Requires-Dist: pydantic (>=2)
23
23
  Requires-Dist: pymongo (>=4.12.0,<5.0.0)
24
24
  Requires-Dist: pypdf (>=5.4.0,<6.0.0)
25
+ Requires-Dist: scrubadub (>=2.0.1,<3.0.0)
25
26
  Requires-Dist: zep-cloud (>=2.10.1,<3.0.0)
26
27
  Project-URL: Documentation, https://docs.solana-agent.com
27
28
  Project-URL: Homepage, https://solana-agent.com
@@ -58,11 +59,13 @@ Build your AI agents in three lines of code!
58
59
  * Intelligent Routing
59
60
  * Business Alignment
60
61
  * Extensible Tooling
62
+ * Automatic Tool Workflows
61
63
  * Knowledge Base
62
64
  * MCP Support
65
+ * Guardrails
63
66
  * Tested & Secure
64
67
  * Built in Python
65
- * Powers [CometHeart](https://cometheart.com) & [WalletBubbles](https://walletbubbles.com)
68
+ * Powers [CometHeart](https://cometheart.com)
66
69
 
67
70
  ## Features
68
71
 
@@ -81,26 +84,26 @@ Build your AI agents in three lines of code!
81
84
  * Powerful tool integration using standard Python packages and/or inline tools
82
85
  * Assigned tools are utilized by agents automatically and effectively
83
86
  * Integrated Knowledge Base with semantic search and automatic PDF chunking
87
+ * Input and output guardrails for content filtering, safety, and data sanitization
88
+ * Automatic sequential tool workflows allowing agents to chain multiple tools
84
89
 
85
90
  ## Stack
86
91
 
87
92
  ### Tech
88
93
 
89
94
  * [Python](https://python.org) - Programming Language
90
- * [OpenAI](https://openai.com), [Google](https://ai.google.dev), [xAI](https://x.ai) - LLM Providers
95
+ * [OpenAI](https://openai.com) - AI Provider
91
96
  * [MongoDB](https://mongodb.com) - Conversational History (optional)
92
97
  * [Zep Cloud](https://getzep.com) - Conversational Memory (optional)
93
98
  * [Pinecone](https://pinecone.io) - Knowledge Base (optional)
94
99
 
95
- ### LLMs
100
+ ### AI Models Used
96
101
 
97
- * [gpt-4.1-mini](https://platform.openai.com/docs/models/gpt-4.1-mini) (agent)
102
+ * [gpt-4.1](https://platform.openai.com/docs/models/gpt-4.1) (agent)
98
103
  * [gpt-4.1-nano](https://platform.openai.com/docs/models/gpt-4.1-nano) (router)
99
104
  * [text-embedding-3-large](https://platform.openai.com/docs/models/text-embedding-3-large) or [text-embedding-3-small](https://platform.openai.com/docs/models/text-embedding-3-small) (embedding)
100
105
  * [tts-1](https://platform.openai.com/docs/models/tts-1) (audio TTS)
101
106
  * [gpt-4o-mini-transcribe](https://platform.openai.com/docs/models/gpt-4o-mini-transcribe) (audio transcription)
102
- * [gemini-2.5-flash-preview](https://ai.google.dev/gemini-api/docs/models#gemini-2.5-flash-preview) (optional)
103
- * [grok-3-mini-fast-beta](https://docs.x.ai/docs/models#models-and-pricing) (optional)
104
107
 
105
108
  ## Installation
106
109
 
@@ -110,13 +113,13 @@ You can install Solana Agent using pip:
110
113
 
111
114
  ## Flows
112
115
 
113
- In both flows of single and multiple agents - it is one user query to one agent using one tool (if needed).
116
+ In both flows of single and multiple agents - it is one user query to one agent using one or many tools (if needed).
114
117
 
115
- An agent can have multiple tools and will choose the best one to answer the user query.
118
+ An agent can have multiple tools and will choose the best ones to fulfill the user's query.
116
119
 
117
- Routing is determined by optimal domain expertise of the agent for the user query.
120
+ Routing is determined by optimal domain expertise of the agent for the user's query.
118
121
 
119
- When the agent uses a tool it feeds the tool output back to itself to generate the final response.
122
+ When the agent uses tools it feeds the tools output back to itself to generate the final response.
120
123
 
121
124
  This is important as tools generally output unstructured and unformatted data that the agent needs to prepare for the user.
122
125
 
@@ -125,13 +128,13 @@ Keep this in mind while designing your agentic systems using Solana Agent.
125
128
  ```ascii
126
129
  Single Agent
127
130
 
128
- ┌────────┐ ┌─────────┐ ┌────────┐
129
- │ │ │ │ │
130
- │ │ │ │ │
131
- │ User │◄──────►│ Agent │◄──────►│ Tool
132
- │ │ │ │ │
133
- │ │ │ │ │
134
- └────────┘ └─────────┘ └────────┘
131
+ ┌────────┐ ┌─────────┐ ┌────────-┐
132
+ │ │ │ │ │
133
+ │ │ │ │ │
134
+ │ User │◄──────►│ Agent │◄──────►│ Tools
135
+ │ │ │ │ │
136
+ │ │ │ │ │
137
+ └────────┘ └─────────┘ └────────-┘
135
138
 
136
139
 
137
140
 
@@ -139,13 +142,13 @@ Keep this in mind while designing your agentic systems using Solana Agent.
139
142
 
140
143
  Multiple Agents
141
144
 
142
- ┌────────┐ ┌──────────┐ ┌─────────┐ ┌────────┐
143
- │ │ │ │ │ │ │
144
- │ │ │ │ │ │ │
145
- ┌───►│ User ├───────►│ Router ├───────►│ Agent │◄──────►│ Tool
146
- │ │ │ │ │ │ │ │
147
- │ │ │ │ │ │ │ │
148
- │ └────────┘ └──────────┘ └────┬────┘ └────────┘
145
+ ┌────────┐ ┌──────────┐ ┌─────────┐ ┌────────-┐
146
+ │ │ │ │ │ │ │
147
+ │ │ │ │ │ │ │
148
+ ┌───►│ User ├───────►│ Router ├───────►│ Agent │◄──────►│ Tools
149
+ │ │ │ │ │ │ │ │
150
+ │ │ │ │ │ │ │ │
151
+ │ └────────┘ └──────────┘ └────┬────┘ └────────-┘
149
152
  │ │
150
153
  │ │
151
154
  │ │
@@ -316,30 +319,6 @@ config = {
316
319
  }
317
320
  ```
318
321
 
319
- ### Gemini
320
-
321
- This allows Gemini to replace OpenAI for agent and router.
322
-
323
- ```python
324
- config = {
325
- "gemini": {
326
- "api_key": "your-gemini-api-key",
327
- },
328
- }
329
- ```
330
-
331
- ### Grok
332
-
333
- This allows Grok to replace OpenAI (or Gemini) for agent.
334
-
335
- ```python
336
- config = {
337
- "grok": {
338
- "api_key": "your-grok-api-key",
339
- },
340
- }
341
- ```
342
-
343
322
  ### Knowledge Base
344
323
 
345
324
  The Knowledge Base (KB) is meant to store text values and/or small PDFs.
@@ -448,12 +427,90 @@ async for response in solana_agent.process("user123", "Summarize the annual repo
448
427
  print(response, end="")
449
428
  ```
450
429
 
430
+ ### Guardrails
431
+
432
+ Guardrails allow you to process and potentially modify user input before it reaches the agent (Input Guardrails) and agent output before it's sent back to the user (Output Guardrails). This is useful for implementing safety checks, content moderation, data sanitization, or custom transformations.
433
+
434
+ Solana Agent provides a built-in PII scrubber based on [scrubadub](https://github.com/LeapBeyond/scrubadub).
435
+
436
+ ```python
437
+ from solana_agent import SolanaAgent
438
+
439
+ config = {
440
+ "guardrails": {
441
+ "input": [
442
+ # Example using a custom input guardrail
443
+ {
444
+ "class": "MyInputGuardrail",
445
+ "config": {"setting1": "value1"}
446
+ },
447
+ # Example using the built-in PII guardrail for input
448
+ {
449
+ "class": "solana_agent.guardrails.pii.PII",
450
+ "config": {
451
+ "locale": "en_GB", # Optional: Specify locale (default: en_US)
452
+ "replacement": "[REDACTED]" # Optional: Custom replacement format
453
+ }
454
+ }
455
+ ],
456
+ "output": [
457
+ # Example using a custom output guardrail
458
+ {
459
+ "class": "MyOutputGuardrail",
460
+ "config": {"filter_level": "high"}
461
+ },
462
+ # Example using the built-in PII guardrail for output (with defaults)
463
+ {
464
+ "class": "solana_agent.guardrails.pii.PII"
465
+ # No config needed to use defaults
466
+ }
467
+ ]
468
+ },
469
+ }
470
+ ```
471
+
472
+ #### Example Custom Guardrails
473
+
474
+ ```python
475
+ from solana_agent import InputGuardrail, OutputGuardrail
476
+ import logging
477
+
478
+ logger = logging.getLogger(__name__)
479
+
480
+ class MyInputGuardrail(InputGuardrail):
481
+ def __init__(self, config=None):
482
+ super().__init__(config)
483
+ self.setting1 = self.config.get("setting1", "default_value")
484
+ logger.info(f"MyInputGuardrail initialized with setting1: {self.setting1}")
485
+
486
+ async def process(self, text: str) -> str:
487
+ # Example: Convert input to lowercase
488
+ processed_text = text.lower()
489
+ logger.debug(f"Input Guardrail processed: {text} -> {processed_text}")
490
+ return processed_text
491
+
492
+ class MyOutputGuardrail(OutputGuardrail):
493
+ def __init__(self, config=None):
494
+ super().__init__(config)
495
+ self.filter_level = self.config.get("filter_level", "low")
496
+ logger.info(f"MyOutputGuardrail initialized with filter_level: {self.filter_level}")
497
+
498
+ async def process(self, text: str) -> str:
499
+ # Example: Basic profanity filtering (replace with a real library)
500
+ if self.filter_level == "high" and "badword" in text:
501
+ processed_text = text.replace("badword", "*******")
502
+ logger.warning(f"Output Guardrail filtered content.")
503
+ return processed_text
504
+ logger.debug("Output Guardrail passed text through.")
505
+ return text
506
+ ```
507
+
451
508
  ## Tools
452
509
 
453
510
  Tools can be used from plugins like Solana Agent Kit (sakit) or via inline tools. Tools available via plugins integrate automatically with Solana Agent.
454
511
 
455
- * Agents can only call one tool per response
456
- * Agents choose the best tool for the job
512
+ * Agents can use multiple tools per response and should apply the right sequential order (like send an email to bob@bob.com with the latest news on Solana)
513
+ * Agents choose the best tools for the job
457
514
  * Solana Agent doesn't use OpenAI function calling (tools) as they don't support async functions
458
515
  * Solana Agent tools are async functions
459
516
 
@@ -528,7 +585,6 @@ Other MCP servers may work but are not supported.
528
585
  `pip install sakit`
529
586
 
530
587
  ```python
531
-
532
588
  from solana_agent import SolanaAgent
533
589
 
534
590
  config = {
@@ -28,11 +28,13 @@ Build your AI agents in three lines of code!
28
28
  * Intelligent Routing
29
29
  * Business Alignment
30
30
  * Extensible Tooling
31
+ * Automatic Tool Workflows
31
32
  * Knowledge Base
32
33
  * MCP Support
34
+ * Guardrails
33
35
  * Tested & Secure
34
36
  * Built in Python
35
- * Powers [CometHeart](https://cometheart.com) & [WalletBubbles](https://walletbubbles.com)
37
+ * Powers [CometHeart](https://cometheart.com)
36
38
 
37
39
  ## Features
38
40
 
@@ -51,26 +53,26 @@ Build your AI agents in three lines of code!
51
53
  * Powerful tool integration using standard Python packages and/or inline tools
52
54
  * Assigned tools are utilized by agents automatically and effectively
53
55
  * Integrated Knowledge Base with semantic search and automatic PDF chunking
56
+ * Input and output guardrails for content filtering, safety, and data sanitization
57
+ * Automatic sequential tool workflows allowing agents to chain multiple tools
54
58
 
55
59
  ## Stack
56
60
 
57
61
  ### Tech
58
62
 
59
63
  * [Python](https://python.org) - Programming Language
60
- * [OpenAI](https://openai.com), [Google](https://ai.google.dev), [xAI](https://x.ai) - LLM Providers
64
+ * [OpenAI](https://openai.com) - AI Provider
61
65
  * [MongoDB](https://mongodb.com) - Conversational History (optional)
62
66
  * [Zep Cloud](https://getzep.com) - Conversational Memory (optional)
63
67
  * [Pinecone](https://pinecone.io) - Knowledge Base (optional)
64
68
 
65
- ### LLMs
69
+ ### AI Models Used
66
70
 
67
- * [gpt-4.1-mini](https://platform.openai.com/docs/models/gpt-4.1-mini) (agent)
71
+ * [gpt-4.1](https://platform.openai.com/docs/models/gpt-4.1) (agent)
68
72
  * [gpt-4.1-nano](https://platform.openai.com/docs/models/gpt-4.1-nano) (router)
69
73
  * [text-embedding-3-large](https://platform.openai.com/docs/models/text-embedding-3-large) or [text-embedding-3-small](https://platform.openai.com/docs/models/text-embedding-3-small) (embedding)
70
74
  * [tts-1](https://platform.openai.com/docs/models/tts-1) (audio TTS)
71
75
  * [gpt-4o-mini-transcribe](https://platform.openai.com/docs/models/gpt-4o-mini-transcribe) (audio transcription)
72
- * [gemini-2.5-flash-preview](https://ai.google.dev/gemini-api/docs/models#gemini-2.5-flash-preview) (optional)
73
- * [grok-3-mini-fast-beta](https://docs.x.ai/docs/models#models-and-pricing) (optional)
74
76
 
75
77
  ## Installation
76
78
 
@@ -80,13 +82,13 @@ You can install Solana Agent using pip:
80
82
 
81
83
  ## Flows
82
84
 
83
- In both flows of single and multiple agents - it is one user query to one agent using one tool (if needed).
85
+ In both flows of single and multiple agents - it is one user query to one agent using one or many tools (if needed).
84
86
 
85
- An agent can have multiple tools and will choose the best one to answer the user query.
87
+ An agent can have multiple tools and will choose the best ones to fulfill the user's query.
86
88
 
87
- Routing is determined by optimal domain expertise of the agent for the user query.
89
+ Routing is determined by optimal domain expertise of the agent for the user's query.
88
90
 
89
- When the agent uses a tool it feeds the tool output back to itself to generate the final response.
91
+ When the agent uses tools it feeds the tools output back to itself to generate the final response.
90
92
 
91
93
  This is important as tools generally output unstructured and unformatted data that the agent needs to prepare for the user.
92
94
 
@@ -95,13 +97,13 @@ Keep this in mind while designing your agentic systems using Solana Agent.
95
97
  ```ascii
96
98
  Single Agent
97
99
 
98
- ┌────────┐ ┌─────────┐ ┌────────┐
99
- │ │ │ │ │
100
- │ │ │ │ │
101
- │ User │◄──────►│ Agent │◄──────►│ Tool
102
- │ │ │ │ │
103
- │ │ │ │ │
104
- └────────┘ └─────────┘ └────────┘
100
+ ┌────────┐ ┌─────────┐ ┌────────-┐
101
+ │ │ │ │ │
102
+ │ │ │ │ │
103
+ │ User │◄──────►│ Agent │◄──────►│ Tools
104
+ │ │ │ │ │
105
+ │ │ │ │ │
106
+ └────────┘ └─────────┘ └────────-┘
105
107
 
106
108
 
107
109
 
@@ -109,13 +111,13 @@ Keep this in mind while designing your agentic systems using Solana Agent.
109
111
 
110
112
  Multiple Agents
111
113
 
112
- ┌────────┐ ┌──────────┐ ┌─────────┐ ┌────────┐
113
- │ │ │ │ │ │ │
114
- │ │ │ │ │ │ │
115
- ┌───►│ User ├───────►│ Router ├───────►│ Agent │◄──────►│ Tool
116
- │ │ │ │ │ │ │ │
117
- │ │ │ │ │ │ │ │
118
- │ └────────┘ └──────────┘ └────┬────┘ └────────┘
114
+ ┌────────┐ ┌──────────┐ ┌─────────┐ ┌────────-┐
115
+ │ │ │ │ │ │ │
116
+ │ │ │ │ │ │ │
117
+ ┌───►│ User ├───────►│ Router ├───────►│ Agent │◄──────►│ Tools
118
+ │ │ │ │ │ │ │ │
119
+ │ │ │ │ │ │ │ │
120
+ │ └────────┘ └──────────┘ └────┬────┘ └────────-┘
119
121
  │ │
120
122
  │ │
121
123
  │ │
@@ -286,30 +288,6 @@ config = {
286
288
  }
287
289
  ```
288
290
 
289
- ### Gemini
290
-
291
- This allows Gemini to replace OpenAI for agent and router.
292
-
293
- ```python
294
- config = {
295
- "gemini": {
296
- "api_key": "your-gemini-api-key",
297
- },
298
- }
299
- ```
300
-
301
- ### Grok
302
-
303
- This allows Grok to replace OpenAI (or Gemini) for agent.
304
-
305
- ```python
306
- config = {
307
- "grok": {
308
- "api_key": "your-grok-api-key",
309
- },
310
- }
311
- ```
312
-
313
291
  ### Knowledge Base
314
292
 
315
293
  The Knowledge Base (KB) is meant to store text values and/or small PDFs.
@@ -418,12 +396,90 @@ async for response in solana_agent.process("user123", "Summarize the annual repo
418
396
  print(response, end="")
419
397
  ```
420
398
 
399
+ ### Guardrails
400
+
401
+ Guardrails allow you to process and potentially modify user input before it reaches the agent (Input Guardrails) and agent output before it's sent back to the user (Output Guardrails). This is useful for implementing safety checks, content moderation, data sanitization, or custom transformations.
402
+
403
+ Solana Agent provides a built-in PII scrubber based on [scrubadub](https://github.com/LeapBeyond/scrubadub).
404
+
405
+ ```python
406
+ from solana_agent import SolanaAgent
407
+
408
+ config = {
409
+ "guardrails": {
410
+ "input": [
411
+ # Example using a custom input guardrail
412
+ {
413
+ "class": "MyInputGuardrail",
414
+ "config": {"setting1": "value1"}
415
+ },
416
+ # Example using the built-in PII guardrail for input
417
+ {
418
+ "class": "solana_agent.guardrails.pii.PII",
419
+ "config": {
420
+ "locale": "en_GB", # Optional: Specify locale (default: en_US)
421
+ "replacement": "[REDACTED]" # Optional: Custom replacement format
422
+ }
423
+ }
424
+ ],
425
+ "output": [
426
+ # Example using a custom output guardrail
427
+ {
428
+ "class": "MyOutputGuardrail",
429
+ "config": {"filter_level": "high"}
430
+ },
431
+ # Example using the built-in PII guardrail for output (with defaults)
432
+ {
433
+ "class": "solana_agent.guardrails.pii.PII"
434
+ # No config needed to use defaults
435
+ }
436
+ ]
437
+ },
438
+ }
439
+ ```
440
+
441
+ #### Example Custom Guardrails
442
+
443
+ ```python
444
+ from solana_agent import InputGuardrail, OutputGuardrail
445
+ import logging
446
+
447
+ logger = logging.getLogger(__name__)
448
+
449
+ class MyInputGuardrail(InputGuardrail):
450
+ def __init__(self, config=None):
451
+ super().__init__(config)
452
+ self.setting1 = self.config.get("setting1", "default_value")
453
+ logger.info(f"MyInputGuardrail initialized with setting1: {self.setting1}")
454
+
455
+ async def process(self, text: str) -> str:
456
+ # Example: Convert input to lowercase
457
+ processed_text = text.lower()
458
+ logger.debug(f"Input Guardrail processed: {text} -> {processed_text}")
459
+ return processed_text
460
+
461
+ class MyOutputGuardrail(OutputGuardrail):
462
+ def __init__(self, config=None):
463
+ super().__init__(config)
464
+ self.filter_level = self.config.get("filter_level", "low")
465
+ logger.info(f"MyOutputGuardrail initialized with filter_level: {self.filter_level}")
466
+
467
+ async def process(self, text: str) -> str:
468
+ # Example: Basic profanity filtering (replace with a real library)
469
+ if self.filter_level == "high" and "badword" in text:
470
+ processed_text = text.replace("badword", "*******")
471
+ logger.warning(f"Output Guardrail filtered content.")
472
+ return processed_text
473
+ logger.debug("Output Guardrail passed text through.")
474
+ return text
475
+ ```
476
+
421
477
  ## Tools
422
478
 
423
479
  Tools can be used from plugins like Solana Agent Kit (sakit) or via inline tools. Tools available via plugins integrate automatically with Solana Agent.
424
480
 
425
- * Agents can only call one tool per response
426
- * Agents choose the best tool for the job
481
+ * Agents can use multiple tools per response and should apply the right sequential order (like send an email to bob@bob.com with the latest news on Solana)
482
+ * Agents choose the best tools for the job
427
483
  * Solana Agent doesn't use OpenAI function calling (tools) as they don't support async functions
428
484
  * Solana Agent tools are async functions
429
485
 
@@ -498,7 +554,6 @@ Other MCP servers may work but are not supported.
498
554
  `pip install sakit`
499
555
 
500
556
  ```python
501
-
502
557
  from solana_agent import SolanaAgent
503
558
 
504
559
  config = {
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "solana-agent"
3
- version = "27.4.3"
3
+ version = "28.0.0"
4
4
  description = "AI Agents for Solana"
5
5
  authors = ["Bevan Hunt <bevan@bevanhunt.com>"]
6
6
  license = "MIT"
@@ -33,6 +33,7 @@ pinecone = "^6.0.2"
33
33
  llama-index-core = "^0.12.30"
34
34
  llama-index-embeddings-openai = "^0.3.1"
35
35
  pypdf = "^5.4.0"
36
+ scrubadub = "^2.0.1"
36
37
 
37
38
  [tool.poetry.group.dev.dependencies]
38
39
  pytest = "^8.3.5"
@@ -5,8 +5,6 @@ This package provides a modular framework for building AI agent systems with
5
5
  multiple specialized agents, memory management, and conversation routing.
6
6
  """
7
7
 
8
- __version__ = "14.0.0" # Update with your actual version
9
-
10
8
  # Client interface (main entry point)
11
9
  from solana_agent.client.solana_agent import SolanaAgent
12
10
 
@@ -17,6 +15,10 @@ from solana_agent.factories.agent_factory import SolanaAgentFactory
17
15
  from solana_agent.plugins.manager import PluginManager
18
16
  from solana_agent.plugins.registry import ToolRegistry
19
17
  from solana_agent.plugins.tools.auto_tool import AutoTool
18
+ from solana_agent.interfaces.guardrails.guardrails import (
19
+ InputGuardrail,
20
+ OutputGuardrail,
21
+ )
20
22
 
21
23
  # Package metadata
22
24
  __all__ = [
@@ -28,4 +30,7 @@ __all__ = [
28
30
  "PluginManager",
29
31
  "ToolRegistry",
30
32
  "AutoTool",
33
+ # Guardrails
34
+ "InputGuardrail",
35
+ "OutputGuardrail",
31
36
  ]
@@ -15,7 +15,7 @@ from solana_agent.interfaces.providers.llm import LLMProvider
15
15
 
16
16
  T = TypeVar("T", bound=BaseModel)
17
17
 
18
- DEFAULT_CHAT_MODEL = "gpt-4.1-mini"
18
+ DEFAULT_CHAT_MODEL = "gpt-4.1"
19
19
  DEFAULT_PARSE_MODEL = "gpt-4.1-nano"
20
20
  DEFAULT_EMBEDDING_MODEL = "text-embedding-3-large"
21
21
  DEFAULT_EMBEDDING_DIMENSIONS = 3072
@@ -129,45 +129,41 @@ class OpenAIAdapter(LLMProvider):
129
129
  api_key: Optional[str] = None,
130
130
  base_url: Optional[str] = None,
131
131
  model: Optional[str] = None,
132
- ) -> AsyncGenerator[str, None]: # pragma: no cover
133
- """Generate text from OpenAI models."""
132
+ ) -> str: # pragma: no cover
133
+ """Generate text from OpenAI models as a single string."""
134
134
  messages = []
135
-
136
135
  if system_prompt:
137
136
  messages.append({"role": "system", "content": system_prompt})
138
-
139
137
  messages.append({"role": "user", "content": prompt})
140
138
 
141
- # Prepare request parameters
139
+ # Prepare request parameters - stream is always False now
142
140
  request_params = {
143
141
  "messages": messages,
144
- "stream": True,
145
- "model": self.text_model,
142
+ "stream": False, # Hardcoded to False
143
+ "model": model or self.text_model,
146
144
  }
147
145
 
146
+ # Determine client based on provided api_key/base_url
148
147
  if api_key and base_url:
149
148
  client = AsyncOpenAI(api_key=api_key, base_url=base_url)
150
149
  else:
151
150
  client = self.client
152
151
 
153
- if model:
154
- request_params["model"] = model
155
-
156
152
  try:
153
+ # Make the non-streaming API call
157
154
  response = await client.chat.completions.create(**request_params)
158
155
 
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
156
+ # Handle non-streaming response
157
+ if response.choices and response.choices[0].message.content:
158
+ full_text = response.choices[0].message.content
159
+ return full_text # Return the complete string
160
+ else:
161
+ print("Received non-streaming response with no content.")
162
+ return "" # Return empty string if no content
164
163
 
165
164
  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)}"
165
+ # Log the error and return an error message string
166
+ print(f"Error in generate_text: {e}")
171
167
 
172
168
  async def parse_structured_output(
173
169
  self,