solana-agent 31.1.2__tar.gz → 31.1.4__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-31.1.2 → solana_agent-31.1.4}/PKG-INFO +1 -1
  2. {solana_agent-31.1.2 → solana_agent-31.1.4}/pyproject.toml +1 -1
  3. {solana_agent-31.1.2 → solana_agent-31.1.4}/solana_agent/services/query.py +97 -24
  4. {solana_agent-31.1.2 → solana_agent-31.1.4}/LICENSE +0 -0
  5. {solana_agent-31.1.2 → solana_agent-31.1.4}/README.md +0 -0
  6. {solana_agent-31.1.2 → solana_agent-31.1.4}/solana_agent/__init__.py +0 -0
  7. {solana_agent-31.1.2 → solana_agent-31.1.4}/solana_agent/adapters/__init__.py +0 -0
  8. {solana_agent-31.1.2 → solana_agent-31.1.4}/solana_agent/adapters/mongodb_adapter.py +0 -0
  9. {solana_agent-31.1.2 → solana_agent-31.1.4}/solana_agent/adapters/openai_adapter.py +0 -0
  10. {solana_agent-31.1.2 → solana_agent-31.1.4}/solana_agent/adapters/pinecone_adapter.py +0 -0
  11. {solana_agent-31.1.2 → solana_agent-31.1.4}/solana_agent/cli.py +0 -0
  12. {solana_agent-31.1.2 → solana_agent-31.1.4}/solana_agent/client/__init__.py +0 -0
  13. {solana_agent-31.1.2 → solana_agent-31.1.4}/solana_agent/client/solana_agent.py +0 -0
  14. {solana_agent-31.1.2 → solana_agent-31.1.4}/solana_agent/domains/__init__.py +0 -0
  15. {solana_agent-31.1.2 → solana_agent-31.1.4}/solana_agent/domains/agent.py +0 -0
  16. {solana_agent-31.1.2 → solana_agent-31.1.4}/solana_agent/domains/routing.py +0 -0
  17. {solana_agent-31.1.2 → solana_agent-31.1.4}/solana_agent/factories/__init__.py +0 -0
  18. {solana_agent-31.1.2 → solana_agent-31.1.4}/solana_agent/factories/agent_factory.py +0 -0
  19. {solana_agent-31.1.2 → solana_agent-31.1.4}/solana_agent/guardrails/pii.py +0 -0
  20. {solana_agent-31.1.2 → solana_agent-31.1.4}/solana_agent/interfaces/__init__.py +0 -0
  21. {solana_agent-31.1.2 → solana_agent-31.1.4}/solana_agent/interfaces/client/client.py +0 -0
  22. {solana_agent-31.1.2 → solana_agent-31.1.4}/solana_agent/interfaces/guardrails/guardrails.py +0 -0
  23. {solana_agent-31.1.2 → solana_agent-31.1.4}/solana_agent/interfaces/plugins/plugins.py +0 -0
  24. {solana_agent-31.1.2 → solana_agent-31.1.4}/solana_agent/interfaces/providers/data_storage.py +0 -0
  25. {solana_agent-31.1.2 → solana_agent-31.1.4}/solana_agent/interfaces/providers/llm.py +0 -0
  26. {solana_agent-31.1.2 → solana_agent-31.1.4}/solana_agent/interfaces/providers/memory.py +0 -0
  27. {solana_agent-31.1.2 → solana_agent-31.1.4}/solana_agent/interfaces/providers/vector_storage.py +0 -0
  28. {solana_agent-31.1.2 → solana_agent-31.1.4}/solana_agent/interfaces/services/agent.py +0 -0
  29. {solana_agent-31.1.2 → solana_agent-31.1.4}/solana_agent/interfaces/services/knowledge_base.py +0 -0
  30. {solana_agent-31.1.2 → solana_agent-31.1.4}/solana_agent/interfaces/services/query.py +0 -0
  31. {solana_agent-31.1.2 → solana_agent-31.1.4}/solana_agent/interfaces/services/routing.py +0 -0
  32. {solana_agent-31.1.2 → solana_agent-31.1.4}/solana_agent/plugins/__init__.py +0 -0
  33. {solana_agent-31.1.2 → solana_agent-31.1.4}/solana_agent/plugins/manager.py +0 -0
  34. {solana_agent-31.1.2 → solana_agent-31.1.4}/solana_agent/plugins/registry.py +0 -0
  35. {solana_agent-31.1.2 → solana_agent-31.1.4}/solana_agent/plugins/tools/__init__.py +0 -0
  36. {solana_agent-31.1.2 → solana_agent-31.1.4}/solana_agent/plugins/tools/auto_tool.py +0 -0
  37. {solana_agent-31.1.2 → solana_agent-31.1.4}/solana_agent/repositories/__init__.py +0 -0
  38. {solana_agent-31.1.2 → solana_agent-31.1.4}/solana_agent/repositories/memory.py +0 -0
  39. {solana_agent-31.1.2 → solana_agent-31.1.4}/solana_agent/services/__init__.py +0 -0
  40. {solana_agent-31.1.2 → solana_agent-31.1.4}/solana_agent/services/agent.py +0 -0
  41. {solana_agent-31.1.2 → solana_agent-31.1.4}/solana_agent/services/knowledge_base.py +0 -0
  42. {solana_agent-31.1.2 → solana_agent-31.1.4}/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.2
3
+ Version: 31.1.4
4
4
  Summary: AI Agents for Solana
5
5
  License: MIT
6
6
  Keywords: solana,solana ai,solana agent,ai,ai agent,ai agents
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "solana-agent"
3
- version = "31.1.2"
3
+ version = "31.1.4"
4
4
  description = "AI Agents for Solana"
5
5
  authors = ["Bevan Hunt <bevan@bevanhunt.com>"]
6
6
  license = "MIT"
@@ -80,7 +80,7 @@ class QueryService(QueryServiceInterface):
80
80
  router: Optional[RoutingServiceInterface] = None,
81
81
  output_model: Optional[Type[BaseModel]] = None,
82
82
  capture_schema: Optional[Dict[str, Any]] = None,
83
- capture_name: Optional[str] = None,
83
+ capture_name: Optional[Dict[str, Any]] = None,
84
84
  ) -> AsyncGenerator[Union[str, bytes, BaseModel], None]: # pragma: no cover
85
85
  """Process the user request and generate a response."""
86
86
  try:
@@ -241,31 +241,102 @@ class QueryService(QueryServiceInterface):
241
241
  options[idx] = label
242
242
  return options
243
243
 
244
- def _detect_field_from_prev_question(
244
+ # LLM-backed field detection (gpt-4.1-mini) with graceful fallbacks
245
+ class _FieldDetect(BaseModel):
246
+ field: Optional[str] = None
247
+
248
+ async def _detect_field_from_prev_question(
245
249
  prev_text: str, schema: Optional[Dict[str, Any]]
246
250
  ) -> Optional[str]:
247
251
  if not prev_text or not isinstance(schema, dict):
248
252
  return None
249
- t = prev_text.lower()
250
- patterns = [
251
- ("ideas", ["which ideas attract you", "ideas"]),
252
- ("description", ["please describe yourself", "describe yourself"]),
253
- ("myself", ["tell us about yourself", "about yourself"]),
254
- ("questions", ["do you have any questions"]),
255
- ("rating", ["rating", "1 to 5", "how satisfied", "how happy"]),
256
- ("email", ["email"]),
257
- ("phone", ["phone"]),
258
- ("name", ["name"]),
259
- ("city", ["city"]),
260
- ("state", ["state"]),
261
- ]
262
- candidates = set((schema.get("properties") or {}).keys())
263
- for field, keys in patterns:
264
- if field in candidates and any(key in t for key in keys):
265
- return field
266
- for field in candidates:
267
- if field in t:
268
- return field
253
+ props = list((schema.get("properties") or {}).keys())
254
+ if not props:
255
+ return None
256
+
257
+ question = prev_text.strip()
258
+ instruction = (
259
+ "You are a strict classifier. Given the assistant's last question and a list of "
260
+ "permitted schema field keys, choose exactly one key that the question is asking the user to answer. "
261
+ "If none apply, return null."
262
+ )
263
+ user_prompt = (
264
+ f"Schema field keys (choose exactly one of these): {props}\n"
265
+ f"Assistant question:\n{question}\n\n"
266
+ 'Return strictly JSON like: {"field": "<one_of_the_keys_or_null>"}'
267
+ )
268
+
269
+ # Try llm_provider.parse_structured_output with mini
270
+ try:
271
+ if hasattr(
272
+ self.agent_service.llm_provider, "parse_structured_output"
273
+ ):
274
+ try:
275
+ result = await self.agent_service.llm_provider.parse_structured_output(
276
+ prompt=user_prompt,
277
+ system_prompt=instruction,
278
+ model_class=_FieldDetect,
279
+ model="gpt-4.1-mini",
280
+ )
281
+ except TypeError:
282
+ # Provider may not accept 'model' kwarg
283
+ result = await self.agent_service.llm_provider.parse_structured_output(
284
+ prompt=user_prompt,
285
+ system_prompt=instruction,
286
+ model_class=_FieldDetect,
287
+ )
288
+ # Read result
289
+ sel = None
290
+ try:
291
+ sel = getattr(result, "field", None)
292
+ except Exception:
293
+ sel = None
294
+ if sel is None:
295
+ try:
296
+ d = result.model_dump()
297
+ sel = d.get("field")
298
+ except Exception:
299
+ sel = None
300
+ if sel in props:
301
+ return sel
302
+ except Exception as e:
303
+ logger.debug(
304
+ f"LLM parse_structured_output field detection failed: {e}"
305
+ )
306
+
307
+ # Fallback: use generate_response with output_model=_FieldDetect
308
+ try:
309
+ async for r in self.agent_service.generate_response(
310
+ agent_name=agent_name,
311
+ user_id=user_id,
312
+ query=user_text,
313
+ images=images,
314
+ memory_context="",
315
+ output_format="text",
316
+ prompt=f"{instruction}\n\n{user_prompt}",
317
+ output_model=_FieldDetect,
318
+ ):
319
+ fd = r
320
+ sel = None
321
+ try:
322
+ sel = fd.field # type: ignore[attr-defined]
323
+ except Exception:
324
+ try:
325
+ d = fd.model_dump()
326
+ sel = d.get("field")
327
+ except Exception:
328
+ sel = None
329
+ if sel in props:
330
+ return sel
331
+ break
332
+ except Exception as e:
333
+ logger.debug(f"LLM generate_response field detection failed: {e}")
334
+
335
+ # Final heuristic fallback (keeps system working if LLM unavailable)
336
+ t = question.lower()
337
+ for key in props:
338
+ if key in t:
339
+ return key
269
340
  return None
270
341
 
271
342
  # Resolve active capture from args or agent config
@@ -334,7 +405,9 @@ class QueryService(QueryServiceInterface):
334
405
  missing_required = _missing(required_fields)
335
406
  missing_optional = _missing(optional_fields)
336
407
 
337
- target_field: Optional[str] = _detect_field_from_prev_question(
408
+ target_field: Optional[
409
+ str
410
+ ] = await _detect_field_from_prev_question(
338
411
  prev_assistant, active_capture_schema
339
412
  )
340
413
  if not target_field:
@@ -758,5 +831,5 @@ class QueryService(QueryServiceInterface):
758
831
  else:
759
832
  fields[field_name] = (typ, default)
760
833
 
761
- Model = create_model(name, **fields)
834
+ Model = create_model(name, **fields) # type: ignore
762
835
  return Model
File without changes
File without changes