solana-agent 31.1.3__tar.gz → 31.1.5__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.
- {solana_agent-31.1.3 → solana_agent-31.1.5}/PKG-INFO +1 -2
- {solana_agent-31.1.3 → solana_agent-31.1.5}/README.md +0 -1
- {solana_agent-31.1.3 → solana_agent-31.1.5}/pyproject.toml +1 -1
- {solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/factories/agent_factory.py +1 -12
- {solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/repositories/memory.py +30 -51
- {solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/services/query.py +209 -16
- {solana_agent-31.1.3 → solana_agent-31.1.5}/LICENSE +0 -0
- {solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/__init__.py +0 -0
- {solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/adapters/__init__.py +0 -0
- {solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/adapters/mongodb_adapter.py +0 -0
- {solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/adapters/openai_adapter.py +0 -0
- {solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/adapters/pinecone_adapter.py +0 -0
- {solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/cli.py +0 -0
- {solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/client/__init__.py +0 -0
- {solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/client/solana_agent.py +0 -0
- {solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/domains/__init__.py +0 -0
- {solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/domains/agent.py +0 -0
- {solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/domains/routing.py +0 -0
- {solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/factories/__init__.py +0 -0
- {solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/guardrails/pii.py +0 -0
- {solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/interfaces/__init__.py +0 -0
- {solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/interfaces/client/client.py +0 -0
- {solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/interfaces/guardrails/guardrails.py +0 -0
- {solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/interfaces/plugins/plugins.py +0 -0
- {solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/interfaces/providers/data_storage.py +0 -0
- {solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/interfaces/providers/llm.py +0 -0
- {solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/interfaces/providers/memory.py +0 -0
- {solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/interfaces/providers/vector_storage.py +0 -0
- {solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/interfaces/services/agent.py +0 -0
- {solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/interfaces/services/knowledge_base.py +0 -0
- {solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/interfaces/services/query.py +0 -0
- {solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/interfaces/services/routing.py +0 -0
- {solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/plugins/__init__.py +0 -0
- {solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/plugins/manager.py +0 -0
- {solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/plugins/registry.py +0 -0
- {solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/plugins/tools/__init__.py +0 -0
- {solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/plugins/tools/auto_tool.py +0 -0
- {solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/repositories/__init__.py +0 -0
- {solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/services/__init__.py +0 -0
- {solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/services/agent.py +0 -0
- {solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/services/knowledge_base.py +0 -0
- {solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/services/routing.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: solana-agent
|
3
|
-
Version: 31.1.
|
3
|
+
Version: 31.1.5
|
4
4
|
Summary: AI Agents for Solana
|
5
5
|
License: MIT
|
6
6
|
Keywords: solana,solana ai,solana agent,ai,ai agent,ai agents
|
@@ -361,7 +361,6 @@ config = {
|
|
361
361
|
"instructions": "You provide friendly, helpful customer support responses.",
|
362
362
|
"specialization": "Customer inquiries",
|
363
363
|
"capture_name": "contact_info",
|
364
|
-
"capture_mode": "once",
|
365
364
|
"capture_schema": {
|
366
365
|
"type": "object",
|
367
366
|
"properties": {
|
@@ -326,7 +326,6 @@ config = {
|
|
326
326
|
"instructions": "You provide friendly, helpful customer support responses.",
|
327
327
|
"specialization": "Customer inquiries",
|
328
328
|
"capture_name": "contact_info",
|
329
|
-
"capture_mode": "once",
|
330
329
|
"capture_schema": {
|
331
330
|
"type": "object",
|
332
331
|
"properties": {
|
@@ -133,12 +133,7 @@ class SolanaAgentFactory:
|
|
133
133
|
voice=org_config.get("voice", ""),
|
134
134
|
)
|
135
135
|
|
136
|
-
#
|
137
|
-
capture_modes: Dict[str, str] = {}
|
138
|
-
for agent in config.get("agents", []):
|
139
|
-
mode = agent.get("capture_mode")
|
140
|
-
if mode in {"once", "multiple"} and agent.get("name"):
|
141
|
-
capture_modes[agent["name"]] = mode
|
136
|
+
# capture_mode removed: repository now always upserts/merges per capture
|
142
137
|
|
143
138
|
# Create repositories
|
144
139
|
memory_provider = None
|
@@ -148,22 +143,16 @@ class SolanaAgentFactory:
|
|
148
143
|
"mongo_adapter": db_adapter,
|
149
144
|
"zep_api_key": config["zep"].get("api_key"),
|
150
145
|
}
|
151
|
-
if capture_modes: # pragma: no cover
|
152
|
-
mem_kwargs["capture_modes"] = capture_modes
|
153
146
|
memory_provider = MemoryRepository(**mem_kwargs)
|
154
147
|
|
155
148
|
if "mongo" in config and "zep" not in config:
|
156
149
|
mem_kwargs = {"mongo_adapter": db_adapter}
|
157
|
-
if capture_modes:
|
158
|
-
mem_kwargs["capture_modes"] = capture_modes
|
159
150
|
memory_provider = MemoryRepository(**mem_kwargs)
|
160
151
|
|
161
152
|
if "zep" in config and "mongo" not in config:
|
162
153
|
if "api_key" not in config["zep"]:
|
163
154
|
raise ValueError("Zep API key is required.")
|
164
155
|
mem_kwargs = {"zep_api_key": config["zep"].get("api_key")}
|
165
|
-
if capture_modes: # pragma: no cover
|
166
|
-
mem_kwargs["capture_modes"] = capture_modes
|
167
156
|
memory_provider = MemoryRepository(**mem_kwargs)
|
168
157
|
|
169
158
|
guardrail_config = config.get("guardrails", {})
|
@@ -19,10 +19,7 @@ class MemoryRepository(MemoryProvider):
|
|
19
19
|
self,
|
20
20
|
mongo_adapter: Optional[MongoDBAdapter] = None,
|
21
21
|
zep_api_key: Optional[str] = None,
|
22
|
-
capture_modes: Optional[Dict[str, str]] = None,
|
23
22
|
):
|
24
|
-
self.capture_modes: Dict[str, str] = capture_modes or {}
|
25
|
-
|
26
23
|
# Mongo setup
|
27
24
|
if not mongo_adapter:
|
28
25
|
self.mongo = None
|
@@ -46,18 +43,15 @@ class MemoryRepository(MemoryProvider):
|
|
46
43
|
self.mongo.create_index(self.captures_collection, [("capture_name", 1)])
|
47
44
|
self.mongo.create_index(self.captures_collection, [("agent_name", 1)])
|
48
45
|
self.mongo.create_index(self.captures_collection, [("timestamp", 1)])
|
49
|
-
# Unique
|
46
|
+
# Unique per user/agent/capture combo
|
50
47
|
try:
|
51
48
|
self.mongo.create_index(
|
52
49
|
self.captures_collection,
|
53
50
|
[("user_id", 1), ("agent_name", 1), ("capture_name", 1)],
|
54
51
|
unique=True,
|
55
|
-
partialFilterExpression={"mode": "once"},
|
56
52
|
)
|
57
53
|
except Exception as e:
|
58
|
-
logger.error(
|
59
|
-
f"Error creating partial unique index for captures: {e}"
|
60
|
-
)
|
54
|
+
logger.error(f"Error creating unique index for captures: {e}")
|
61
55
|
except Exception as e:
|
62
56
|
logger.error(f"Error initializing MongoDB captures collection: {e}")
|
63
57
|
self.captures_collection = "captures"
|
@@ -223,54 +217,39 @@ class MemoryRepository(MemoryProvider):
|
|
223
217
|
raise ValueError("data must be a dictionary")
|
224
218
|
|
225
219
|
try:
|
226
|
-
mode = self.capture_modes.get(agent_name, "once") if agent_name else "once"
|
227
220
|
now = datetime.now(timezone.utc)
|
228
|
-
|
229
|
-
|
221
|
+
key = {
|
222
|
+
"user_id": user_id,
|
223
|
+
"agent_name": agent_name,
|
224
|
+
"capture_name": capture_name,
|
225
|
+
}
|
226
|
+
existing = self.mongo.find_one(self.captures_collection, key)
|
227
|
+
merged_data: Dict[str, Any] = {}
|
228
|
+
if existing and isinstance(existing.get("data"), dict):
|
229
|
+
merged_data.update(existing.get("data", {}))
|
230
|
+
merged_data.update(data or {})
|
231
|
+
update_doc = {
|
232
|
+
"$set": {
|
230
233
|
"user_id": user_id,
|
231
234
|
"agent_name": agent_name,
|
232
235
|
"capture_name": capture_name,
|
233
|
-
"data":
|
234
|
-
"schema":
|
235
|
-
|
236
|
+
"data": merged_data,
|
237
|
+
"schema": (
|
238
|
+
schema
|
239
|
+
if schema is not None
|
240
|
+
else existing.get("schema")
|
241
|
+
if existing
|
242
|
+
else {}
|
243
|
+
),
|
236
244
|
"timestamp": now,
|
237
|
-
|
238
|
-
}
|
239
|
-
|
240
|
-
|
241
|
-
key =
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
}
|
246
|
-
existing = self.mongo.find_one(self.captures_collection, key)
|
247
|
-
merged_data: Dict[str, Any] = {}
|
248
|
-
if existing and isinstance(existing.get("data"), dict):
|
249
|
-
merged_data.update(existing.get("data", {}))
|
250
|
-
merged_data.update(data or {})
|
251
|
-
update_doc = {
|
252
|
-
"$set": {
|
253
|
-
"user_id": user_id,
|
254
|
-
"agent_name": agent_name,
|
255
|
-
"capture_name": capture_name,
|
256
|
-
"data": merged_data,
|
257
|
-
"schema": (
|
258
|
-
schema
|
259
|
-
if schema is not None
|
260
|
-
else existing.get("schema")
|
261
|
-
if existing
|
262
|
-
else {}
|
263
|
-
),
|
264
|
-
"mode": "once",
|
265
|
-
"timestamp": now,
|
266
|
-
},
|
267
|
-
"$setOnInsert": {"created_at": now},
|
268
|
-
}
|
269
|
-
self.mongo.update_one(
|
270
|
-
self.captures_collection, key, update_doc, upsert=True
|
271
|
-
)
|
272
|
-
doc = self.mongo.find_one(self.captures_collection, key)
|
273
|
-
return str(doc.get("_id")) if doc and doc.get("_id") else None
|
245
|
+
},
|
246
|
+
"$setOnInsert": {"created_at": now},
|
247
|
+
}
|
248
|
+
self.mongo.update_one(
|
249
|
+
self.captures_collection, key, update_doc, upsert=True
|
250
|
+
)
|
251
|
+
doc = self.mongo.find_one(self.captures_collection, key)
|
252
|
+
return str(doc.get("_id")) if doc and doc.get("_id") else None
|
274
253
|
except Exception as e: # pragma: no cover
|
275
254
|
logger.error(f"MongoDB save_capture error: {e}")
|
276
255
|
return None
|
@@ -8,7 +8,18 @@ clean separation of concerns.
|
|
8
8
|
|
9
9
|
import logging
|
10
10
|
import re
|
11
|
-
|
11
|
+
import time
|
12
|
+
from typing import (
|
13
|
+
Any,
|
14
|
+
AsyncGenerator,
|
15
|
+
Dict,
|
16
|
+
List,
|
17
|
+
Literal,
|
18
|
+
Optional,
|
19
|
+
Type,
|
20
|
+
Union,
|
21
|
+
Tuple,
|
22
|
+
)
|
12
23
|
|
13
24
|
from pydantic import BaseModel
|
14
25
|
|
@@ -50,6 +61,151 @@ class QueryService(QueryServiceInterface):
|
|
50
61
|
self.knowledge_base = knowledge_base
|
51
62
|
self.kb_results_count = kb_results_count
|
52
63
|
self.input_guardrails = input_guardrails or []
|
64
|
+
# Per-user sticky sessions (in-memory)
|
65
|
+
# { user_id: { 'agent': str, 'started_at': float, 'last_updated': float, 'required_complete': bool } }
|
66
|
+
self._sticky_sessions: Dict[str, Dict[str, Any]] = {}
|
67
|
+
|
68
|
+
def _get_sticky_agent(self, user_id: str) -> Optional[str]:
|
69
|
+
sess = self._sticky_sessions.get(user_id)
|
70
|
+
return sess.get("agent") if isinstance(sess, dict) else None
|
71
|
+
|
72
|
+
def _set_sticky_agent(
|
73
|
+
self, user_id: str, agent_name: str, required_complete: bool = False
|
74
|
+
) -> None:
|
75
|
+
self._sticky_sessions[user_id] = {
|
76
|
+
"agent": agent_name,
|
77
|
+
"started_at": self._sticky_sessions.get(user_id, {}).get(
|
78
|
+
"started_at", time.time()
|
79
|
+
),
|
80
|
+
"last_updated": time.time(),
|
81
|
+
"required_complete": required_complete,
|
82
|
+
}
|
83
|
+
|
84
|
+
def _update_sticky_required_complete(
|
85
|
+
self, user_id: str, required_complete: bool
|
86
|
+
) -> None:
|
87
|
+
if user_id in self._sticky_sessions:
|
88
|
+
self._sticky_sessions[user_id]["required_complete"] = required_complete
|
89
|
+
self._sticky_sessions[user_id]["last_updated"] = time.time()
|
90
|
+
|
91
|
+
def _clear_sticky_agent(self, user_id: str) -> None:
|
92
|
+
if user_id in self._sticky_sessions:
|
93
|
+
del self._sticky_sessions[user_id]
|
94
|
+
|
95
|
+
# LLM-backed switch intent detection (gpt-4.1-mini)
|
96
|
+
class _SwitchIntentModel(BaseModel):
|
97
|
+
switch: bool = False
|
98
|
+
target_agent: Optional[str] = None
|
99
|
+
start_new: bool = False
|
100
|
+
|
101
|
+
async def _detect_switch_intent(
|
102
|
+
self, text: str, available_agents: List[str]
|
103
|
+
) -> Tuple[bool, Optional[str], bool]:
|
104
|
+
"""Detect if the user is asking to switch agents or start a new conversation.
|
105
|
+
|
106
|
+
Returns: (switch_requested, target_agent_name_or_none, start_new_conversation)
|
107
|
+
Implemented as an LLM call to gpt-4.1-mini with structured output.
|
108
|
+
"""
|
109
|
+
if not text:
|
110
|
+
return (False, None, False)
|
111
|
+
|
112
|
+
# Instruction and user prompt for the classifier
|
113
|
+
instruction = (
|
114
|
+
"You are a strict intent classifier for agent routing. "
|
115
|
+
"Decide if the user's message requests switching to another agent or starting a new conversation. "
|
116
|
+
"Only return JSON with keys: switch (bool), target_agent (string|null), start_new (bool). "
|
117
|
+
"If a target agent is mentioned, it MUST be one of the provided agent names (case-insensitive). "
|
118
|
+
"If none clearly applies, set switch=false and start_new=false and target_agent=null."
|
119
|
+
)
|
120
|
+
user_prompt = (
|
121
|
+
f"Available agents (choose only from these if a target is specified): {available_agents}\n\n"
|
122
|
+
f"User message:\n{text}\n\n"
|
123
|
+
'Return JSON only, like: {"switch": true|false, "target_agent": "<one_of_available_or_null>", "start_new": true|false}'
|
124
|
+
)
|
125
|
+
|
126
|
+
# Primary: use llm_provider.parse_structured_output
|
127
|
+
try:
|
128
|
+
if hasattr(self.agent_service.llm_provider, "parse_structured_output"):
|
129
|
+
try:
|
130
|
+
result = (
|
131
|
+
await self.agent_service.llm_provider.parse_structured_output(
|
132
|
+
prompt=user_prompt,
|
133
|
+
system_prompt=instruction,
|
134
|
+
model_class=QueryService._SwitchIntentModel,
|
135
|
+
model="gpt-4.1-mini",
|
136
|
+
)
|
137
|
+
)
|
138
|
+
except TypeError:
|
139
|
+
# Provider may not accept 'model' kwarg
|
140
|
+
result = (
|
141
|
+
await self.agent_service.llm_provider.parse_structured_output(
|
142
|
+
prompt=user_prompt,
|
143
|
+
system_prompt=instruction,
|
144
|
+
model_class=QueryService._SwitchIntentModel,
|
145
|
+
)
|
146
|
+
)
|
147
|
+
switch = bool(getattr(result, "switch", False))
|
148
|
+
target = getattr(result, "target_agent", None)
|
149
|
+
start_new = bool(getattr(result, "start_new", False))
|
150
|
+
# Normalize target to available agent name
|
151
|
+
if target:
|
152
|
+
target_lower = target.lower()
|
153
|
+
norm = None
|
154
|
+
for a in available_agents:
|
155
|
+
if a.lower() == target_lower or target_lower in a.lower():
|
156
|
+
norm = a
|
157
|
+
break
|
158
|
+
target = norm
|
159
|
+
if not switch:
|
160
|
+
target = None
|
161
|
+
return (switch, target, start_new)
|
162
|
+
except Exception as e:
|
163
|
+
logger.debug(f"LLM switch intent parse_structured_output failed: {e}")
|
164
|
+
|
165
|
+
# Fallback: generate_response with output_model
|
166
|
+
try:
|
167
|
+
async for r in self.agent_service.generate_response(
|
168
|
+
agent_name="default",
|
169
|
+
user_id="router",
|
170
|
+
query="",
|
171
|
+
images=None,
|
172
|
+
memory_context="",
|
173
|
+
output_format="text",
|
174
|
+
prompt=f"{instruction}\n\n{user_prompt}",
|
175
|
+
output_model=QueryService._SwitchIntentModel,
|
176
|
+
):
|
177
|
+
result = r
|
178
|
+
switch = False
|
179
|
+
target = None
|
180
|
+
start_new = False
|
181
|
+
try:
|
182
|
+
switch = bool(result.switch) # type: ignore[attr-defined]
|
183
|
+
target = result.target_agent # type: ignore[attr-defined]
|
184
|
+
start_new = bool(result.start_new) # type: ignore[attr-defined]
|
185
|
+
except Exception:
|
186
|
+
try:
|
187
|
+
d = result.model_dump()
|
188
|
+
switch = bool(d.get("switch", False))
|
189
|
+
target = d.get("target_agent")
|
190
|
+
start_new = bool(d.get("start_new", False))
|
191
|
+
except Exception:
|
192
|
+
pass
|
193
|
+
if target:
|
194
|
+
target_lower = str(target).lower()
|
195
|
+
norm = None
|
196
|
+
for a in available_agents:
|
197
|
+
if a.lower() == target_lower or target_lower in a.lower():
|
198
|
+
norm = a
|
199
|
+
break
|
200
|
+
target = norm
|
201
|
+
if not switch:
|
202
|
+
target = None
|
203
|
+
return (switch, target, start_new)
|
204
|
+
except Exception as e:
|
205
|
+
logger.debug(f"LLM switch intent generate_response failed: {e}")
|
206
|
+
|
207
|
+
# Last resort: no switch
|
208
|
+
return (False, None, False)
|
53
209
|
|
54
210
|
async def process(
|
55
211
|
self,
|
@@ -80,7 +236,7 @@ class QueryService(QueryServiceInterface):
|
|
80
236
|
router: Optional[RoutingServiceInterface] = None,
|
81
237
|
output_model: Optional[Type[BaseModel]] = None,
|
82
238
|
capture_schema: Optional[Dict[str, Any]] = None,
|
83
|
-
capture_name: Optional[
|
239
|
+
capture_name: Optional[str] = None,
|
84
240
|
) -> AsyncGenerator[Union[str, bytes, BaseModel], None]: # pragma: no cover
|
85
241
|
"""Process the user request and generate a response."""
|
86
242
|
try:
|
@@ -164,7 +320,7 @@ class QueryService(QueryServiceInterface):
|
|
164
320
|
except Exception:
|
165
321
|
kb_context = ""
|
166
322
|
|
167
|
-
# 6)
|
323
|
+
# 6) Determine agent (sticky session aware; allow explicit switch/new conversation)
|
168
324
|
agent_name = "default"
|
169
325
|
prev_assistant = ""
|
170
326
|
routing_input = user_text
|
@@ -184,19 +340,52 @@ class QueryService(QueryServiceInterface):
|
|
184
340
|
"assistant_message", ""
|
185
341
|
) or ""
|
186
342
|
if prev_user_msg:
|
187
|
-
routing_input =
|
188
|
-
f"previous_user_message: {prev_user_msg}\n"
|
189
|
-
f"current_user_message: {user_text}"
|
190
|
-
)
|
343
|
+
routing_input = f"previous_user_message: {prev_user_msg}\ncurrent_user_message: {user_text}"
|
191
344
|
except Exception:
|
192
345
|
pass
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
346
|
+
|
347
|
+
# Get available agents first so the LLM can select a valid target
|
348
|
+
agents = self.agent_service.get_all_ai_agents() or {}
|
349
|
+
available_agent_names = list(agents.keys())
|
350
|
+
|
351
|
+
# LLM detects switch intent
|
352
|
+
(
|
353
|
+
switch_requested,
|
354
|
+
requested_agent_raw,
|
355
|
+
start_new,
|
356
|
+
) = await self._detect_switch_intent(user_text, available_agent_names)
|
357
|
+
|
358
|
+
# Normalize requested agent to an exact available key
|
359
|
+
requested_agent = None
|
360
|
+
if requested_agent_raw:
|
361
|
+
raw_lower = requested_agent_raw.lower()
|
362
|
+
for a in available_agent_names:
|
363
|
+
if a.lower() == raw_lower or raw_lower in a.lower():
|
364
|
+
requested_agent = a
|
365
|
+
break
|
366
|
+
|
367
|
+
sticky_agent = self._get_sticky_agent(user_id)
|
368
|
+
|
369
|
+
if sticky_agent and not switch_requested:
|
370
|
+
agent_name = sticky_agent
|
371
|
+
else:
|
372
|
+
try:
|
373
|
+
if start_new:
|
374
|
+
# Start fresh
|
375
|
+
self._clear_sticky_agent(user_id)
|
376
|
+
if requested_agent:
|
377
|
+
agent_name = requested_agent
|
378
|
+
else:
|
379
|
+
# Route if no explicit target
|
380
|
+
if router:
|
381
|
+
agent_name = await router.route_query(routing_input)
|
382
|
+
else:
|
383
|
+
agent_name = await self.routing_service.route_query(
|
384
|
+
routing_input
|
385
|
+
)
|
386
|
+
except Exception:
|
387
|
+
agent_name = next(iter(agents.keys())) if agents else "default"
|
388
|
+
self._set_sticky_agent(user_id, agent_name, required_complete=False)
|
200
389
|
|
201
390
|
# 7) Captured data context + incremental save using previous assistant message
|
202
391
|
capture_context = ""
|
@@ -276,7 +465,7 @@ class QueryService(QueryServiceInterface):
|
|
276
465
|
prompt=user_prompt,
|
277
466
|
system_prompt=instruction,
|
278
467
|
model_class=_FieldDetect,
|
279
|
-
model="gpt-4.1-
|
468
|
+
model="gpt-4.1-mini",
|
280
469
|
)
|
281
470
|
except TypeError:
|
282
471
|
# Provider may not accept 'model' kwarg
|
@@ -285,7 +474,6 @@ class QueryService(QueryServiceInterface):
|
|
285
474
|
system_prompt=instruction,
|
286
475
|
model_class=_FieldDetect,
|
287
476
|
)
|
288
|
-
# Read result
|
289
477
|
sel = None
|
290
478
|
try:
|
291
479
|
sel = getattr(result, "field", None)
|
@@ -544,6 +732,11 @@ class QueryService(QueryServiceInterface):
|
|
544
732
|
|
545
733
|
if lines:
|
546
734
|
capture_context = "\n".join(lines) + "\n\n"
|
735
|
+
# Update sticky session completion flag
|
736
|
+
try:
|
737
|
+
self._update_sticky_required_complete(user_id, required_complete)
|
738
|
+
except Exception:
|
739
|
+
pass
|
547
740
|
|
548
741
|
# Merge contexts + flow rules
|
549
742
|
combined_context = ""
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/interfaces/guardrails/guardrails.py
RENAMED
File without changes
|
File without changes
|
{solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/interfaces/providers/data_storage.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
{solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/interfaces/providers/vector_storage.py
RENAMED
File without changes
|
File without changes
|
{solana_agent-31.1.3 → solana_agent-31.1.5}/solana_agent/interfaces/services/knowledge_base.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|