solana-agent 31.2.1__tar.gz → 31.2.2__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 (47) hide show
  1. {solana_agent-31.2.1 → solana_agent-31.2.2}/PKG-INFO +1 -1
  2. {solana_agent-31.2.1 → solana_agent-31.2.2}/pyproject.toml +1 -1
  3. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/adapters/openai_realtime_ws.py +33 -7
  4. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/services/query.py +6 -0
  5. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/services/realtime.py +14 -0
  6. {solana_agent-31.2.1 → solana_agent-31.2.2}/LICENSE +0 -0
  7. {solana_agent-31.2.1 → solana_agent-31.2.2}/README.md +0 -0
  8. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/__init__.py +0 -0
  9. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/adapters/__init__.py +0 -0
  10. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/adapters/ffmpeg_transcoder.py +0 -0
  11. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/adapters/mongodb_adapter.py +0 -0
  12. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/adapters/openai_adapter.py +0 -0
  13. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/adapters/pinecone_adapter.py +0 -0
  14. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/cli.py +0 -0
  15. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/client/__init__.py +0 -0
  16. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/client/solana_agent.py +0 -0
  17. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/domains/__init__.py +0 -0
  18. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/domains/agent.py +0 -0
  19. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/domains/routing.py +0 -0
  20. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/factories/__init__.py +0 -0
  21. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/factories/agent_factory.py +0 -0
  22. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/guardrails/pii.py +0 -0
  23. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/interfaces/__init__.py +0 -0
  24. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/interfaces/client/client.py +0 -0
  25. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/interfaces/guardrails/guardrails.py +0 -0
  26. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/interfaces/plugins/plugins.py +0 -0
  27. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/interfaces/providers/audio.py +0 -0
  28. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/interfaces/providers/data_storage.py +0 -0
  29. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/interfaces/providers/llm.py +0 -0
  30. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/interfaces/providers/memory.py +0 -0
  31. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/interfaces/providers/realtime.py +0 -0
  32. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/interfaces/providers/vector_storage.py +0 -0
  33. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/interfaces/services/agent.py +0 -0
  34. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/interfaces/services/knowledge_base.py +0 -0
  35. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/interfaces/services/query.py +0 -0
  36. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/interfaces/services/routing.py +0 -0
  37. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/plugins/__init__.py +0 -0
  38. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/plugins/manager.py +0 -0
  39. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/plugins/registry.py +0 -0
  40. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/plugins/tools/__init__.py +0 -0
  41. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/plugins/tools/auto_tool.py +0 -0
  42. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/repositories/__init__.py +0 -0
  43. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/repositories/memory.py +0 -0
  44. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/services/__init__.py +0 -0
  45. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/services/agent.py +0 -0
  46. {solana_agent-31.2.1 → solana_agent-31.2.2}/solana_agent/services/knowledge_base.py +0 -0
  47. {solana_agent-31.2.1 → solana_agent-31.2.2}/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.2.1
3
+ Version: 31.2.2
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.2.1"
3
+ version = "31.2.2"
4
4
  description = "AI Agents for Solana"
5
5
  authors = ["Bevan Hunt <bevan@bevanhunt.com>"]
6
6
  license = "MIT"
@@ -961,9 +961,6 @@ class OpenAIRealtimeWebSocketSession(BaseRealtimeSession):
961
961
  if audio_patch:
962
962
  patch["audio"] = audio_patch
963
963
 
964
- # Always include session.type in updates
965
- patch["type"] = "realtime"
966
-
967
964
  # No top-level turn_detection
968
965
 
969
966
  def _strip_tool_strict(tools_val):
@@ -1030,7 +1027,8 @@ class OpenAIRealtimeWebSocketSession(BaseRealtimeSession):
1030
1027
  )
1031
1028
  except Exception:
1032
1029
  pass
1033
- await self._send(payload)
1030
+ # Use tracked send to attach an event_id and improve diagnostics
1031
+ await self._send_tracked(payload, label="session.update:patch")
1034
1032
 
1035
1033
  async def append_audio(self, pcm16_bytes: bytes) -> None: # pragma: no cover
1036
1034
  b64 = base64.b64encode(pcm16_bytes).decode("ascii")
@@ -1045,10 +1043,16 @@ class OpenAIRealtimeWebSocketSession(BaseRealtimeSession):
1045
1043
 
1046
1044
  async def commit_input(self) -> None: # pragma: no cover
1047
1045
  try:
1048
- # Skip commits while a response is active to avoid server errors
1046
+ # If a previous response is still marked active, wait briefly, then proceed.
1047
+ # Skipping commits here can cause new turns to reference old audio and repeat answers.
1049
1048
  if bool(getattr(self, "_response_active", False)):
1050
- logger.warning("Realtime WS: skipping commit; response active")
1051
- return
1049
+ logger.warning(
1050
+ "Realtime WS: response active at commit; waiting briefly before proceeding"
1051
+ )
1052
+ for _ in range(5): # up to ~0.5s
1053
+ await asyncio.sleep(0.1)
1054
+ if not bool(getattr(self, "_response_active", False)):
1055
+ break
1052
1056
  # Avoid overlapping commits while awaiting server ack
1053
1057
  if bool(getattr(self, "_commit_inflight", False)):
1054
1058
  logger.warning("Realtime WS: skipping commit; commit in-flight")
@@ -1250,6 +1254,24 @@ class OpenAIRealtimeWebSocketSession(BaseRealtimeSession):
1250
1254
  def set_tool_executor(self, executor): # pragma: no cover
1251
1255
  self._tool_executor = executor
1252
1256
 
1257
+ def reset_output_stream(self) -> None: # pragma: no cover
1258
+ """Drain any queued output audio and clear per-response text buffers.
1259
+ This avoids replaying stale audio if the client failed to consume previous chunks."""
1260
+ try:
1261
+ while True:
1262
+ try:
1263
+ _ = self._audio_queue.get_nowait()
1264
+ except asyncio.QueueEmpty:
1265
+ break
1266
+ except Exception:
1267
+ break
1268
+ try:
1269
+ self._out_text_buffers.clear()
1270
+ except Exception:
1271
+ pass
1272
+ except Exception:
1273
+ pass
1274
+
1253
1275
  # Expose whether a function/tool call is currently pending
1254
1276
  def has_pending_tool_call(self) -> bool: # pragma: no cover
1255
1277
  try:
@@ -1611,3 +1633,7 @@ class OpenAITranscriptionWebSocketSession(BaseRealtimeSession):
1611
1633
  def set_tool_executor(self, executor): # pragma: no cover
1612
1634
  # Not applicable for transcription-only
1613
1635
  return
1636
+
1637
+ def reset_output_stream(self) -> None: # pragma: no cover
1638
+ # No audio output stream to reset
1639
+ return
@@ -669,6 +669,12 @@ class QueryService(QueryServiceInterface):
669
669
  await rt.clear_input()
670
670
  except Exception:
671
671
  pass
672
+ # Also reset any leftover output audio so new turn doesn't replay old chunks
673
+ try:
674
+ if hasattr(rt, "reset_output_stream"):
675
+ rt.reset_output_stream()
676
+ except Exception:
677
+ pass
672
678
 
673
679
  # Persist once per turn
674
680
  turn_id = await self.realtime_begin_turn(user_id)
@@ -185,6 +185,13 @@ class RealtimeService:
185
185
  def iter_output_audio(self) -> AsyncGenerator[bytes, None]: # pragma: no cover
186
186
  return self._session.iter_output_audio()
187
187
 
188
+ def reset_output_stream(self) -> None: # pragma: no cover
189
+ try:
190
+ if hasattr(self._session, "reset_output_stream"):
191
+ self._session.reset_output_stream()
192
+ except Exception:
193
+ pass
194
+
188
195
  async def iter_output_audio_encoded(
189
196
  self,
190
197
  ) -> AsyncGenerator[bytes, None]: # pragma: no cover
@@ -447,6 +454,13 @@ class TwinRealtimeService:
447
454
  def iter_output_audio(self) -> AsyncGenerator[bytes, None]: # pragma: no cover
448
455
  return self._conv.iter_output_audio()
449
456
 
457
+ def reset_output_stream(self) -> None: # pragma: no cover
458
+ try:
459
+ if hasattr(self._conv, "reset_output_stream"):
460
+ self._conv.reset_output_stream()
461
+ except Exception:
462
+ pass
463
+
450
464
  async def iter_output_audio_encoded(
451
465
  self,
452
466
  ) -> AsyncGenerator[bytes, None]: # pragma: no cover
File without changes
File without changes