solana-agent 27.4.3__py3-none-any.whl → 27.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- solana_agent/__init__.py +7 -2
- solana_agent/factories/agent_factory.py +53 -1
- solana_agent/guardrails/pii.py +107 -0
- solana_agent/interfaces/guardrails/guardrails.py +26 -0
- solana_agent/services/agent.py +392 -237
- solana_agent/services/query.py +140 -58
- {solana_agent-27.4.3.dist-info → solana_agent-27.5.0.dist-info}/METADATA +83 -3
- {solana_agent-27.4.3.dist-info → solana_agent-27.5.0.dist-info}/RECORD +10 -8
- {solana_agent-27.4.3.dist-info → solana_agent-27.5.0.dist-info}/LICENSE +0 -0
- {solana_agent-27.4.3.dist-info → solana_agent-27.5.0.dist-info}/WHEEL +0 -0
solana_agent/services/query.py
CHANGED
@@ -6,16 +6,29 @@ other services to provide comprehensive responses while maintaining
|
|
6
6
|
clean separation of concerns.
|
7
7
|
"""
|
8
8
|
|
9
|
-
|
9
|
+
import logging
|
10
|
+
from typing import Any, AsyncGenerator, Dict, List, Literal, Optional, Union
|
10
11
|
|
12
|
+
# Interface imports
|
11
13
|
from solana_agent.interfaces.services.query import QueryService as QueryServiceInterface
|
12
14
|
from solana_agent.interfaces.services.routing import (
|
13
15
|
RoutingService as RoutingServiceInterface,
|
14
16
|
)
|
17
|
+
from solana_agent.interfaces.providers.memory import (
|
18
|
+
MemoryProvider as MemoryProviderInterface,
|
19
|
+
)
|
20
|
+
from solana_agent.interfaces.services.knowledge_base import (
|
21
|
+
KnowledgeBaseService as KnowledgeBaseInterface,
|
22
|
+
)
|
23
|
+
from solana_agent.interfaces.guardrails.guardrails import (
|
24
|
+
InputGuardrail,
|
25
|
+
) # <-- Import InputGuardrail
|
26
|
+
|
27
|
+
# Service imports (assuming AgentService is the concrete implementation)
|
15
28
|
from solana_agent.services.agent import AgentService
|
16
29
|
from solana_agent.services.routing import RoutingService
|
17
|
-
|
18
|
-
|
30
|
+
|
31
|
+
logger = logging.getLogger(__name__)
|
19
32
|
|
20
33
|
|
21
34
|
class QueryService(QueryServiceInterface):
|
@@ -25,9 +38,10 @@ class QueryService(QueryServiceInterface):
|
|
25
38
|
self,
|
26
39
|
agent_service: AgentService,
|
27
40
|
routing_service: RoutingService,
|
28
|
-
memory_provider: Optional[
|
29
|
-
knowledge_base: Optional[
|
41
|
+
memory_provider: Optional[MemoryProviderInterface] = None,
|
42
|
+
knowledge_base: Optional[KnowledgeBaseInterface] = None,
|
30
43
|
kb_results_count: int = 3,
|
44
|
+
input_guardrails: List[InputGuardrail] = None,
|
31
45
|
):
|
32
46
|
"""Initialize the query service.
|
33
47
|
|
@@ -35,12 +49,16 @@ class QueryService(QueryServiceInterface):
|
|
35
49
|
agent_service: Service for AI agent management
|
36
50
|
routing_service: Service for routing queries to appropriate agents
|
37
51
|
memory_provider: Optional provider for memory storage and retrieval
|
52
|
+
knowledge_base: Optional provider for knowledge base interactions
|
53
|
+
kb_results_count: Number of results to retrieve from knowledge base
|
54
|
+
input_guardrails: List of input guardrail instances
|
38
55
|
"""
|
39
56
|
self.agent_service = agent_service
|
40
57
|
self.routing_service = routing_service
|
41
58
|
self.memory_provider = memory_provider
|
42
59
|
self.knowledge_base = knowledge_base
|
43
60
|
self.kb_results_count = kb_results_count
|
61
|
+
self.input_guardrails = input_guardrails or [] # <-- Store guardrails
|
44
62
|
|
45
63
|
async def process(
|
46
64
|
self,
|
@@ -69,7 +87,7 @@ class QueryService(QueryServiceInterface):
|
|
69
87
|
prompt: Optional[str] = None,
|
70
88
|
router: Optional[RoutingServiceInterface] = None,
|
71
89
|
) -> AsyncGenerator[Union[str, bytes], None]: # pragma: no cover
|
72
|
-
"""Process the user request with appropriate agent.
|
90
|
+
"""Process the user request with appropriate agent and apply input guardrails.
|
73
91
|
|
74
92
|
Args:
|
75
93
|
user_id: User ID
|
@@ -86,21 +104,48 @@ class QueryService(QueryServiceInterface):
|
|
86
104
|
Response chunks (text strings or audio bytes)
|
87
105
|
"""
|
88
106
|
try:
|
89
|
-
# Handle
|
107
|
+
# --- 1. Handle Audio Input & Extract Text ---
|
90
108
|
user_text = ""
|
91
109
|
if not isinstance(query, str):
|
110
|
+
logger.info(
|
111
|
+
f"Received audio input, transcribing format: {audio_input_format}"
|
112
|
+
)
|
92
113
|
async for (
|
93
114
|
transcript
|
94
115
|
) in self.agent_service.llm_provider.transcribe_audio(
|
95
116
|
query, audio_input_format
|
96
117
|
):
|
97
118
|
user_text += transcript
|
119
|
+
logger.info(f"Transcription result length: {len(user_text)}")
|
98
120
|
else:
|
99
121
|
user_text = query
|
122
|
+
logger.info(f"Received text input length: {len(user_text)}")
|
123
|
+
|
124
|
+
# --- 2. Apply Input Guardrails ---
|
125
|
+
original_text = user_text
|
126
|
+
processed_text = user_text
|
127
|
+
for guardrail in self.input_guardrails:
|
128
|
+
try:
|
129
|
+
processed_text = await guardrail.process(processed_text)
|
130
|
+
logger.debug(
|
131
|
+
f"Applied input guardrail: {guardrail.__class__.__name__}"
|
132
|
+
)
|
133
|
+
except Exception as e:
|
134
|
+
logger.error(
|
135
|
+
f"Error applying input guardrail {guardrail.__class__.__name__}: {e}",
|
136
|
+
exc_info=True,
|
137
|
+
)
|
138
|
+
if processed_text != original_text:
|
139
|
+
logger.info(
|
140
|
+
f"Input guardrails modified user text. Original length: {len(original_text)}, New length: {len(processed_text)}"
|
141
|
+
)
|
142
|
+
user_text = processed_text # Use the processed text going forward
|
143
|
+
# --- End Apply Input Guardrails ---
|
100
144
|
|
101
|
-
# Handle
|
145
|
+
# --- 3. Handle Simple Greetings ---
|
102
146
|
if user_text.strip().lower() in ["test", "hello", "hi", "hey", "ping"]:
|
103
147
|
response = "Hello! How can I help you today?"
|
148
|
+
logger.info("Handling simple greeting.")
|
104
149
|
if output_format == "audio":
|
105
150
|
async for chunk in self.agent_service.llm_provider.tts(
|
106
151
|
text=response,
|
@@ -112,25 +157,32 @@ class QueryService(QueryServiceInterface):
|
|
112
157
|
else:
|
113
158
|
yield response
|
114
159
|
|
115
|
-
# Store simple interaction in memory
|
160
|
+
# Store simple interaction in memory (using processed user_text)
|
116
161
|
if self.memory_provider:
|
117
162
|
await self._store_conversation(user_id, user_text, response)
|
118
163
|
return
|
119
164
|
|
120
|
-
# Get
|
165
|
+
# --- 4. Get Memory Context ---
|
121
166
|
memory_context = ""
|
122
167
|
if self.memory_provider:
|
123
|
-
|
168
|
+
try:
|
169
|
+
memory_context = await self.memory_provider.retrieve(user_id)
|
170
|
+
logger.info(
|
171
|
+
f"Retrieved memory context length: {len(memory_context)}"
|
172
|
+
)
|
173
|
+
except Exception as e:
|
174
|
+
logger.error(f"Error retrieving memory context: {e}", exc_info=True)
|
124
175
|
|
125
|
-
#
|
176
|
+
# --- 5. Retrieve Relevant Knowledge ---
|
126
177
|
kb_context = ""
|
127
178
|
if self.knowledge_base:
|
128
179
|
try:
|
180
|
+
# Use processed user_text for KB query
|
129
181
|
kb_results = await self.knowledge_base.query(
|
130
182
|
query_text=user_text,
|
131
183
|
top_k=self.kb_results_count,
|
132
184
|
include_content=True,
|
133
|
-
include_metadata=False,
|
185
|
+
include_metadata=False, # Keep metadata minimal for context
|
134
186
|
)
|
135
187
|
|
136
188
|
if kb_results:
|
@@ -138,36 +190,47 @@ class QueryService(QueryServiceInterface):
|
|
138
190
|
for i, result in enumerate(kb_results, 1):
|
139
191
|
content = result.get("content", "").strip()
|
140
192
|
kb_context += f"[{i}] {content}\n\n"
|
193
|
+
logger.info(
|
194
|
+
f"Retrieved {len(kb_results)} results from Knowledge Base."
|
195
|
+
)
|
196
|
+
else:
|
197
|
+
logger.info("No relevant results found in Knowledge Base.")
|
141
198
|
except Exception as e:
|
142
|
-
|
199
|
+
logger.error(f"Error retrieving knowledge: {e}", exc_info=True)
|
143
200
|
|
144
|
-
#
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
201
|
+
# --- 6. Route Query ---
|
202
|
+
agent_name = "default" # Fallback agent
|
203
|
+
try:
|
204
|
+
# Use processed user_text for routing
|
205
|
+
if router:
|
206
|
+
agent_name = await router.route_query(user_text)
|
207
|
+
else:
|
208
|
+
agent_name = await self.routing_service.route_query(user_text)
|
209
|
+
logger.info(f"Routed query to agent: {agent_name}")
|
210
|
+
except Exception as e:
|
211
|
+
logger.error(
|
212
|
+
f"Error during routing, falling back to default agent: {e}",
|
213
|
+
exc_info=True,
|
214
|
+
)
|
149
215
|
|
150
|
-
#
|
216
|
+
# --- 7. Combine Context ---
|
151
217
|
combined_context = ""
|
152
218
|
if memory_context:
|
153
|
-
# Add a note about memory priority
|
154
219
|
combined_context += f"CONVERSATION HISTORY (Use for context, but prioritize tools/KB for facts):\n{memory_context}\n\n"
|
155
220
|
if kb_context:
|
156
|
-
# Keep KB context strong
|
157
221
|
combined_context += f"{kb_context}\n"
|
158
222
|
|
159
|
-
# Add an overall instruction about prioritization if both are present
|
160
223
|
if memory_context or kb_context:
|
161
224
|
combined_context += "CRITICAL PRIORITIZATION GUIDE: For factual or current information, prioritize Knowledge Base results and Tool results (if applicable) over Conversation History.\n\n"
|
225
|
+
logger.debug(f"Combined context length: {len(combined_context)}")
|
162
226
|
|
163
|
-
|
164
|
-
|
165
|
-
# Generate response
|
227
|
+
# --- 8. Generate Response ---
|
228
|
+
# Pass the processed user_text to the agent service
|
166
229
|
if output_format == "audio":
|
167
230
|
async for audio_chunk in self.agent_service.generate_response(
|
168
231
|
agent_name=agent_name,
|
169
232
|
user_id=user_id,
|
170
|
-
query=user_text,
|
233
|
+
query=user_text, # Pass processed text
|
171
234
|
memory_context=combined_context,
|
172
235
|
output_format="audio",
|
173
236
|
audio_voice=audio_voice,
|
@@ -177,6 +240,7 @@ class QueryService(QueryServiceInterface):
|
|
177
240
|
):
|
178
241
|
yield audio_chunk
|
179
242
|
|
243
|
+
# Store conversation using processed user_text
|
180
244
|
if self.memory_provider:
|
181
245
|
await self._store_conversation(
|
182
246
|
user_id=user_id,
|
@@ -188,7 +252,7 @@ class QueryService(QueryServiceInterface):
|
|
188
252
|
async for chunk in self.agent_service.generate_response(
|
189
253
|
agent_name=agent_name,
|
190
254
|
user_id=user_id,
|
191
|
-
query=user_text,
|
255
|
+
query=user_text, # Pass processed text
|
192
256
|
memory_context=combined_context,
|
193
257
|
output_format="text",
|
194
258
|
prompt=prompt,
|
@@ -196,6 +260,7 @@ class QueryService(QueryServiceInterface):
|
|
196
260
|
yield chunk
|
197
261
|
full_text_response += chunk
|
198
262
|
|
263
|
+
# Store conversation using processed user_text
|
199
264
|
if self.memory_provider and full_text_response:
|
200
265
|
await self._store_conversation(
|
201
266
|
user_id=user_id,
|
@@ -204,22 +269,28 @@ class QueryService(QueryServiceInterface):
|
|
204
269
|
)
|
205
270
|
|
206
271
|
except Exception as e:
|
207
|
-
|
272
|
+
import traceback
|
273
|
+
|
274
|
+
error_msg = (
|
275
|
+
"I apologize for the technical difficulty. Please try again later."
|
276
|
+
)
|
277
|
+
logger.error(f"Error in query processing: {e}\n{traceback.format_exc()}")
|
278
|
+
|
208
279
|
if output_format == "audio":
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
280
|
+
try:
|
281
|
+
async for chunk in self.agent_service.llm_provider.tts(
|
282
|
+
text=error_msg,
|
283
|
+
voice=audio_voice,
|
284
|
+
response_format=audio_output_format,
|
285
|
+
):
|
286
|
+
yield chunk
|
287
|
+
except Exception as tts_e:
|
288
|
+
logger.error(f"Error during TTS for error message: {tts_e}")
|
289
|
+
# Fallback to yielding text error if TTS fails
|
290
|
+
yield error_msg + f" (TTS Error: {tts_e})"
|
215
291
|
else:
|
216
292
|
yield error_msg
|
217
293
|
|
218
|
-
print(f"Error in query processing: {str(e)}")
|
219
|
-
import traceback
|
220
|
-
|
221
|
-
print(traceback.format_exc())
|
222
|
-
|
223
294
|
async def delete_user_history(self, user_id: str) -> None:
|
224
295
|
"""Delete all conversation history for a user.
|
225
296
|
|
@@ -229,8 +300,15 @@ class QueryService(QueryServiceInterface):
|
|
229
300
|
if self.memory_provider:
|
230
301
|
try:
|
231
302
|
await self.memory_provider.delete(user_id)
|
303
|
+
logger.info(f"Deleted conversation history for user: {user_id}")
|
232
304
|
except Exception as e:
|
233
|
-
|
305
|
+
logger.error(
|
306
|
+
f"Error deleting user history for {user_id}: {e}", exc_info=True
|
307
|
+
)
|
308
|
+
else:
|
309
|
+
logger.warning(
|
310
|
+
"Attempted to delete user history, but no memory provider is configured."
|
311
|
+
)
|
234
312
|
|
235
313
|
async def get_user_history(
|
236
314
|
self,
|
@@ -248,17 +326,12 @@ class QueryService(QueryServiceInterface):
|
|
248
326
|
sort_order: Sort order ("asc" or "desc")
|
249
327
|
|
250
328
|
Returns:
|
251
|
-
Dictionary with paginated results and metadata
|
252
|
-
{
|
253
|
-
"data": List of conversation entries,
|
254
|
-
"total": Total number of entries,
|
255
|
-
"page": Current page number,
|
256
|
-
"page_size": Number of items per page,
|
257
|
-
"total_pages": Total number of pages,
|
258
|
-
"error": Error message if any
|
259
|
-
}
|
329
|
+
Dictionary with paginated results and metadata.
|
260
330
|
"""
|
261
331
|
if not self.memory_provider:
|
332
|
+
logger.warning(
|
333
|
+
"Attempted to get user history, but no memory provider is configured."
|
334
|
+
)
|
262
335
|
return {
|
263
336
|
"data": [],
|
264
337
|
"total": 0,
|
@@ -278,7 +351,7 @@ class QueryService(QueryServiceInterface):
|
|
278
351
|
)
|
279
352
|
|
280
353
|
# Calculate total pages
|
281
|
-
total_pages = (total + page_size - 1) // page_size
|
354
|
+
total_pages = (total + page_size - 1) // page_size if total > 0 else 0
|
282
355
|
|
283
356
|
# Get paginated results
|
284
357
|
conversations = self.memory_provider.find(
|
@@ -292,13 +365,11 @@ class QueryService(QueryServiceInterface):
|
|
292
365
|
# Format the results
|
293
366
|
formatted_conversations = []
|
294
367
|
for conv in conversations:
|
295
|
-
# Convert datetime to Unix timestamp (seconds since epoch)
|
296
368
|
timestamp = (
|
297
369
|
int(conv.get("timestamp").timestamp())
|
298
370
|
if conv.get("timestamp")
|
299
371
|
else None
|
300
372
|
)
|
301
|
-
|
302
373
|
formatted_conversations.append(
|
303
374
|
{
|
304
375
|
"id": str(conv.get("_id")),
|
@@ -308,6 +379,9 @@ class QueryService(QueryServiceInterface):
|
|
308
379
|
}
|
309
380
|
)
|
310
381
|
|
382
|
+
logger.info(
|
383
|
+
f"Retrieved page {page_num}/{total_pages} of history for user {user_id}"
|
384
|
+
)
|
311
385
|
return {
|
312
386
|
"data": formatted_conversations,
|
313
387
|
"total": total,
|
@@ -318,10 +392,11 @@ class QueryService(QueryServiceInterface):
|
|
318
392
|
}
|
319
393
|
|
320
394
|
except Exception as e:
|
321
|
-
print(f"Error retrieving user history: {str(e)}")
|
322
395
|
import traceback
|
323
396
|
|
324
|
-
|
397
|
+
logger.error(
|
398
|
+
f"Error retrieving user history for {user_id}: {e}\n{traceback.format_exc()}"
|
399
|
+
)
|
325
400
|
return {
|
326
401
|
"data": [],
|
327
402
|
"total": 0,
|
@@ -338,8 +413,8 @@ class QueryService(QueryServiceInterface):
|
|
338
413
|
|
339
414
|
Args:
|
340
415
|
user_id: User ID
|
341
|
-
user_message: User message
|
342
|
-
assistant_message: Assistant message
|
416
|
+
user_message: User message (potentially processed by input guardrails)
|
417
|
+
assistant_message: Assistant message (potentially processed by output guardrails)
|
343
418
|
"""
|
344
419
|
if self.memory_provider:
|
345
420
|
try:
|
@@ -350,5 +425,12 @@ class QueryService(QueryServiceInterface):
|
|
350
425
|
{"role": "assistant", "content": assistant_message},
|
351
426
|
],
|
352
427
|
)
|
428
|
+
logger.info(f"Stored conversation for user {user_id}")
|
353
429
|
except Exception as e:
|
354
|
-
|
430
|
+
logger.error(
|
431
|
+
f"Error storing conversation for user {user_id}: {e}", exc_info=True
|
432
|
+
)
|
433
|
+
else:
|
434
|
+
logger.debug(
|
435
|
+
"Memory provider not configured, skipping conversation storage."
|
436
|
+
)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: solana-agent
|
3
|
-
Version: 27.
|
3
|
+
Version: 27.5.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
|
@@ -60,9 +61,10 @@ Build your AI agents in three lines of code!
|
|
60
61
|
* Extensible Tooling
|
61
62
|
* Knowledge Base
|
62
63
|
* MCP Support
|
64
|
+
* Guardrails
|
63
65
|
* Tested & Secure
|
64
66
|
* Built in Python
|
65
|
-
* Powers [CometHeart](https://cometheart.com)
|
67
|
+
* Powers [CometHeart](https://cometheart.com)
|
66
68
|
|
67
69
|
## Features
|
68
70
|
|
@@ -81,6 +83,7 @@ Build your AI agents in three lines of code!
|
|
81
83
|
* Powerful tool integration using standard Python packages and/or inline tools
|
82
84
|
* Assigned tools are utilized by agents automatically and effectively
|
83
85
|
* Integrated Knowledge Base with semantic search and automatic PDF chunking
|
86
|
+
* Input and output guardrails for content filtering, safety, and data sanitization
|
84
87
|
|
85
88
|
## Stack
|
86
89
|
|
@@ -448,6 +451,84 @@ async for response in solana_agent.process("user123", "Summarize the annual repo
|
|
448
451
|
print(response, end="")
|
449
452
|
```
|
450
453
|
|
454
|
+
### Guardrails
|
455
|
+
|
456
|
+
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.
|
457
|
+
|
458
|
+
Solana Agent provides a built-in PII scrubber based on [scrubadub](https://github.com/LeapBeyond/scrubadub).
|
459
|
+
|
460
|
+
```python
|
461
|
+
from solana_agent import SolanaAgent
|
462
|
+
|
463
|
+
config = {
|
464
|
+
"guardrails": {
|
465
|
+
"input": [
|
466
|
+
# Example using a custom input guardrail
|
467
|
+
{
|
468
|
+
"class": "MyInputGuardrail",
|
469
|
+
"config": {"setting1": "value1"}
|
470
|
+
},
|
471
|
+
# Example using the built-in PII guardrail for input
|
472
|
+
{
|
473
|
+
"class": "solana_agent.guardrails.pii.PII",
|
474
|
+
"config": {
|
475
|
+
"locale": "en_GB", # Optional: Specify locale (default: en_US)
|
476
|
+
"replacement": "[REDACTED]" # Optional: Custom replacement format
|
477
|
+
}
|
478
|
+
}
|
479
|
+
],
|
480
|
+
"output": [
|
481
|
+
# Example using a custom output guardrail
|
482
|
+
{
|
483
|
+
"class": "MyOutputGuardrail",
|
484
|
+
"config": {"filter_level": "high"}
|
485
|
+
},
|
486
|
+
# Example using the built-in PII guardrail for output (with defaults)
|
487
|
+
{
|
488
|
+
"class": "solana_agent.guardrails.pii.PII"
|
489
|
+
# No config needed to use defaults
|
490
|
+
}
|
491
|
+
]
|
492
|
+
},
|
493
|
+
}
|
494
|
+
```
|
495
|
+
|
496
|
+
#### Example Custom Guardrails
|
497
|
+
|
498
|
+
```python
|
499
|
+
from solana_agent import InputGuardrail, OutputGuardrail
|
500
|
+
import logging
|
501
|
+
|
502
|
+
logger = logging.getLogger(__name__)
|
503
|
+
|
504
|
+
class MyInputGuardrail(InputGuardrail):
|
505
|
+
def __init__(self, config=None):
|
506
|
+
super().__init__(config)
|
507
|
+
self.setting1 = self.config.get("setting1", "default_value")
|
508
|
+
logger.info(f"MyInputGuardrail initialized with setting1: {self.setting1}")
|
509
|
+
|
510
|
+
async def process(self, text: str) -> str:
|
511
|
+
# Example: Convert input to lowercase
|
512
|
+
processed_text = text.lower()
|
513
|
+
logger.debug(f"Input Guardrail processed: {text} -> {processed_text}")
|
514
|
+
return processed_text
|
515
|
+
|
516
|
+
class MyOutputGuardrail(OutputGuardrail):
|
517
|
+
def __init__(self, config=None):
|
518
|
+
super().__init__(config)
|
519
|
+
self.filter_level = self.config.get("filter_level", "low")
|
520
|
+
logger.info(f"MyOutputGuardrail initialized with filter_level: {self.filter_level}")
|
521
|
+
|
522
|
+
async def process(self, text: str) -> str:
|
523
|
+
# Example: Basic profanity filtering (replace with a real library)
|
524
|
+
if self.filter_level == "high" and "badword" in text:
|
525
|
+
processed_text = text.replace("badword", "*******")
|
526
|
+
logger.warning(f"Output Guardrail filtered content.")
|
527
|
+
return processed_text
|
528
|
+
logger.debug("Output Guardrail passed text through.")
|
529
|
+
return text
|
530
|
+
```
|
531
|
+
|
451
532
|
## Tools
|
452
533
|
|
453
534
|
Tools can be used from plugins like Solana Agent Kit (sakit) or via inline tools. Tools available via plugins integrate automatically with Solana Agent.
|
@@ -528,7 +609,6 @@ Other MCP servers may work but are not supported.
|
|
528
609
|
`pip install sakit`
|
529
610
|
|
530
611
|
```python
|
531
|
-
|
532
612
|
from solana_agent import SolanaAgent
|
533
613
|
|
534
614
|
config = {
|
@@ -1,4 +1,4 @@
|
|
1
|
-
solana_agent/__init__.py,sha256=
|
1
|
+
solana_agent/__init__.py,sha256=g83qhMOCwcWL19V4CYbQwl0Ykpb0xn49OUh05i-pu3g,1001
|
2
2
|
solana_agent/adapters/__init__.py,sha256=tiEEuuy0NF3ngc_tGEcRTt71zVI58v3dYY9RvMrF2Cg,204
|
3
3
|
solana_agent/adapters/mongodb_adapter.py,sha256=0KWIa6kaFbUFvtKUzuV_0p0RFlPPGKrDVIEU2McVY3k,2734
|
4
4
|
solana_agent/adapters/openai_adapter.py,sha256=NZ35mJ80yVWTbdQOAYUh7hDzOFclgfdeJ1Z8v_gfQG8,10922
|
@@ -9,9 +9,11 @@ solana_agent/domains/__init__.py,sha256=HiC94wVPRy-QDJSSRywCRrhrFfTBeHjfi5z-QfZv
|
|
9
9
|
solana_agent/domains/agent.py,sha256=3Q1wg4eIul0CPpaYBOjEthKTfcdhf1SAiWc2R-IMGO8,2561
|
10
10
|
solana_agent/domains/routing.py,sha256=1yR4IswGcmREGgbOOI6TKCfuM7gYGOhQjLkBqnZ-rNo,582
|
11
11
|
solana_agent/factories/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
12
|
-
solana_agent/factories/agent_factory.py,sha256=
|
12
|
+
solana_agent/factories/agent_factory.py,sha256=AsQTS2aMvt25OiQUyYsmIVsp3VLqBbnxD58qcpJ2Urk,14115
|
13
|
+
solana_agent/guardrails/pii.py,sha256=FCz1IC3mmkr41QFFf5NaC0fwJrVkwFsxgyOCS2POO5I,4428
|
13
14
|
solana_agent/interfaces/__init__.py,sha256=IQs1WIM1FeKP1-kY2FEfyhol_dB-I-VAe2rD6jrVF6k,355
|
14
15
|
solana_agent/interfaces/client/client.py,sha256=hsvaQiQdz3MLMNc77oD6ocvvnyl7Ez2n087ptFDA19M,3687
|
16
|
+
solana_agent/interfaces/guardrails/guardrails.py,sha256=gZCQ1FrirW-mX6s7FoYrbRs6golsp-x269kk4kQiZzc,572
|
15
17
|
solana_agent/interfaces/plugins/plugins.py,sha256=Rz52cWBLdotwf4kV-2mC79tRYlN29zHSu1z9-y1HVPk,3329
|
16
18
|
solana_agent/interfaces/providers/data_storage.py,sha256=Y92Cq8BtC55VlsYLD7bo3ofqQabNnlg7Q4H1Q6CDsLU,1713
|
17
19
|
solana_agent/interfaces/providers/llm.py,sha256=SPCXsnCXj7p04E24xB0Wj1q36h2Ci4mmcNCkpHGS8LY,2417
|
@@ -29,11 +31,11 @@ solana_agent/plugins/tools/auto_tool.py,sha256=uihijtlc9CCqCIaRcwPuuN7o1SHIpWL2G
|
|
29
31
|
solana_agent/repositories/__init__.py,sha256=fP83w83CGzXLnSdq-C5wbw9EhWTYtqE2lQTgp46-X_4,163
|
30
32
|
solana_agent/repositories/memory.py,sha256=YYpCyiDVi3a5ZOFYFkzBS6MDjo9g2TnwbEZ5KKfKbII,7204
|
31
33
|
solana_agent/services/__init__.py,sha256=iko0c2MlF8b_SA_nuBGFllr2E3g_JowOrOzGcnU9tkA,162
|
32
|
-
solana_agent/services/agent.py,sha256=
|
34
|
+
solana_agent/services/agent.py,sha256=7JRI8TcZVoVCDeIeGY9el-ingL-lRPrOneJO1uQmWFQ,35652
|
33
35
|
solana_agent/services/knowledge_base.py,sha256=J9V8dNoCCcko3EasiGwK2JJ_A_oG_e-Ni9pgNg0T6wA,33486
|
34
|
-
solana_agent/services/query.py,sha256=
|
36
|
+
solana_agent/services/query.py,sha256=bAoUfe_2EBVEVeh99-2E9KZ0zaHUzf7Lqel3rlHyNX8,17459
|
35
37
|
solana_agent/services/routing.py,sha256=-0fNIKDtCn0-TLUYDFYAE4jPLMeI_jCXIpgtgWDpdf8,6986
|
36
|
-
solana_agent-27.
|
37
|
-
solana_agent-27.
|
38
|
-
solana_agent-27.
|
39
|
-
solana_agent-27.
|
38
|
+
solana_agent-27.5.0.dist-info/LICENSE,sha256=BnSRc-NSFuyF2s496l_4EyrwAP6YimvxWcjPiJ0J7g4,1057
|
39
|
+
solana_agent-27.5.0.dist-info/METADATA,sha256=3hSaou5hzrEL1jowM6_KsofHg_VnMFnbNf7ORPGFJUM,26977
|
40
|
+
solana_agent-27.5.0.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
|
41
|
+
solana_agent-27.5.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|