synapse-cli-agent 0.1.13__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.
- synapse/__init__.py +13 -0
- synapse/__main__.py +6 -0
- synapse/app/__init__.py +1 -0
- synapse/app/agent.py +492 -0
- synapse/app/agent_md.py +107 -0
- synapse/cli.py +750 -0
- synapse/commands/__init__.py +1 -0
- synapse/commands/compression.py +573 -0
- synapse/commands/helpers.py +22 -0
- synapse/commands/mcp.py +406 -0
- synapse/commands/model.py +173 -0
- synapse/commands/result.py +34 -0
- synapse/commands/sessions.py +443 -0
- synapse/commands/slash_cmds.py +521 -0
- synapse/commands/slash_complete.py +816 -0
- synapse/commands/theme.py +99 -0
- synapse/config.py +27 -0
- synapse/content/__init__.py +1 -0
- synapse/content/input_history.py +122 -0
- synapse/content/multimodal.py +733 -0
- synapse/content/prompts.py +249 -0
- synapse/content/skills_catalog.py +128 -0
- synapse/integrations/__init__.py +1 -0
- synapse/integrations/checkpoint_seed.py +281 -0
- synapse/integrations/codex_history.py +375 -0
- synapse/integrations/codex_import.py +393 -0
- synapse/integrations/codex_sessions.py +629 -0
- synapse/integrations/describe_image.py +370 -0
- synapse/integrations/http_clients.py +199 -0
- synapse/integrations/llm_openai_compat.py +90 -0
- synapse/integrations/llm_openai_websocket.py +187 -0
- synapse/integrations/mcp_client.py +646 -0
- synapse/integrations/vision_middleware.py +62 -0
- synapse/models/__init__.py +5 -0
- synapse/models/config.py +240 -0
- synapse/models/helpers.py +206 -0
- synapse/models/profile.py +59 -0
- synapse/models/registry.py +722 -0
- synapse/models_registry.py +7 -0
- synapse/observability/__init__.py +1 -0
- synapse/observability/startup_trace.py +127 -0
- synapse/runtime/__init__.py +1 -0
- synapse/runtime/async_runtime.py +176 -0
- synapse/runtime/backends.py +458 -0
- synapse/runtime/context_compact.py +249 -0
- synapse/runtime/execute_capture.py +48 -0
- synapse/runtime/fs_permissions.py +79 -0
- synapse/runtime/harness.py +57 -0
- synapse/runtime/hitl.py +197 -0
- synapse/runtime/interaction_ledger.py +82 -0
- synapse/runtime/middleware.py +802 -0
- synapse/runtime/model_request_compression_middleware.py +745 -0
- synapse/runtime/pathing.py +146 -0
- synapse/runtime/safety.py +184 -0
- synapse/runtime/steer.py +240 -0
- synapse/runtime/subagents.py +207 -0
- synapse/runtime/tool_ignore.py +221 -0
- synapse/runtime/tool_output_eval.py +118 -0
- synapse/runtime/tool_output_middleware.py +585 -0
- synapse/runtime/tool_output_usage_middleware.py +60 -0
- synapse/sessions/__init__.py +31 -0
- synapse/sessions/cancel_repair.py +208 -0
- synapse/sessions/session_recap.py +174 -0
- synapse/sessions/store.py +695 -0
- synapse/sessions/transcript.py +754 -0
- synapse/settings/__init__.py +5 -0
- synapse/settings/config_paths.py +184 -0
- synapse/settings/schema.py +464 -0
- synapse/tool_output/__init__.py +59 -0
- synapse/tool_output/detection.py +170 -0
- synapse/tool_output/metrics.py +32 -0
- synapse/tool_output/models.py +173 -0
- synapse/tool_output/pipeline.py +330 -0
- synapse/tool_output/repository.py +721 -0
- synapse/tool_output/transformers.py +648 -0
- synapse/tools/__init__.py +5 -0
- synapse/tools/session_tools.py +204 -0
- synapse/ui/__init__.py +10 -0
- synapse/ui/bottombar/__init__.py +73 -0
- synapse/ui/bottombar/components/__init__.py +143 -0
- synapse/ui/bottombar/components/key_hints.py +30 -0
- synapse/ui/bottombar/components/mcp.py +64 -0
- synapse/ui/bottombar/components/mode.py +24 -0
- synapse/ui/bottombar/components/model.py +28 -0
- synapse/ui/bottombar/components/thread.py +29 -0
- synapse/ui/bottombar/context.py +36 -0
- synapse/ui/bottombar/core.py +74 -0
- synapse/ui/dialogs/__init__.py +25 -0
- synapse/ui/dialogs/base.py +362 -0
- synapse/ui/dialogs/codex_session_list.py +84 -0
- synapse/ui/dialogs/compression_diagnostics.py +210 -0
- synapse/ui/dialogs/git_explore.py +702 -0
- synapse/ui/dialogs/mcp_panel.py +407 -0
- synapse/ui/dialogs/model_picker.py +128 -0
- synapse/ui/dialogs/safety_panel.py +63 -0
- synapse/ui/dialogs/session_list.py +98 -0
- synapse/ui/dialogs/theme_designer.py +863 -0
- synapse/ui/dialogs/theme_picker.py +113 -0
- synapse/ui/git_explore/__init__.py +31 -0
- synapse/ui/git_explore/engine.py +82 -0
- synapse/ui/git_explore/provider.py +242 -0
- synapse/ui/git_explore/unified.py +85 -0
- synapse/ui/rendering.py +350 -0
- synapse/ui/sink.py +70 -0
- synapse/ui/steer_widget.py +367 -0
- synapse/ui/stream.py +1207 -0
- synapse/ui/stream_events.py +421 -0
- synapse/ui/stream_runtime.py +252 -0
- synapse/ui/theme.py +1154 -0
- synapse/ui/timeline.py +621 -0
- synapse/ui/topbar/__init__.py +97 -0
- synapse/ui/topbar/components/__init__.py +150 -0
- synapse/ui/topbar/components/branch.py +41 -0
- synapse/ui/topbar/components/title.py +24 -0
- synapse/ui/topbar/components/tool_output.py +24 -0
- synapse/ui/topbar/components/usage.py +24 -0
- synapse/ui/topbar/components/workspace.py +32 -0
- synapse/ui/topbar/context.py +32 -0
- synapse/ui/topbar/core.py +979 -0
- synapse/ui/topbar/git_changes_popover.py +178 -0
- synapse/ui/topbar/git_chrome.py +475 -0
- synapse/ui/topbar/tool_output_popover.py +84 -0
- synapse/ui/topbar/widget.py +474 -0
- synapse/ui/tui.py +5717 -0
- synapse/ui/turn_rail.py +71 -0
- synapse/ui/user_turn.py +83 -0
- synapse/ui/welcome.py +261 -0
- synapse_cli_agent-0.1.13.dist-info/METADATA +412 -0
- synapse_cli_agent-0.1.13.dist-info/RECORD +131 -0
- synapse_cli_agent-0.1.13.dist-info/WHEEL +4 -0
- synapse_cli_agent-0.1.13.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"""LangChain ChatOpenAI adapter for the Responses API WebSocket transport."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
from collections.abc import AsyncIterator
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from langchain_core.callbacks import AsyncCallbackManagerForLLMRun
|
|
10
|
+
from langchain_core.language_models.chat_models import agenerate_from_stream
|
|
11
|
+
from langchain_core.messages import BaseMessage
|
|
12
|
+
from langchain_core.outputs import ChatGenerationChunk, ChatResult
|
|
13
|
+
from langchain_openai import ChatOpenAI
|
|
14
|
+
from langchain_openai.chat_models.base import _convert_responses_chunk_to_generation_chunk
|
|
15
|
+
from pydantic import PrivateAttr
|
|
16
|
+
|
|
17
|
+
_TERMINAL_EVENT_TYPES = {
|
|
18
|
+
"response.completed",
|
|
19
|
+
"response.failed",
|
|
20
|
+
"response.incomplete",
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def prepare_responses_websocket_event(payload: dict[str, Any]) -> dict[str, Any]:
|
|
25
|
+
"""Convert a LangChain Responses HTTP payload into ``response.create``."""
|
|
26
|
+
event = dict(payload)
|
|
27
|
+
event.pop("stream", None)
|
|
28
|
+
event.pop("background", None)
|
|
29
|
+
extra_body = event.pop("extra_body", None)
|
|
30
|
+
if isinstance(extra_body, dict):
|
|
31
|
+
event.update(extra_body)
|
|
32
|
+
# ``thinking`` is a Chat Completions extension used by some compatible
|
|
33
|
+
# providers. Responses WebSocket servers reject it; LangChain has already
|
|
34
|
+
# mapped reasoning effort to the standard top-level ``reasoning`` field.
|
|
35
|
+
event.pop("thinking", None)
|
|
36
|
+
event["type"] = "response.create"
|
|
37
|
+
return event
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class ResponsesWebSocketChatOpenAI(ChatOpenAI):
|
|
41
|
+
"""ChatOpenAI using a reusable ordinary Responses API WebSocket.
|
|
42
|
+
|
|
43
|
+
The public LangChain model contract is unchanged. Only the Responses streaming
|
|
44
|
+
transport is replaced; message conversion and chunk conversion continue to use
|
|
45
|
+
langchain-openai's native implementation.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
_responses_ws_manager: Any = PrivateAttr(default=None)
|
|
49
|
+
_responses_ws_connection: Any = PrivateAttr(default=None)
|
|
50
|
+
_responses_ws_lock: asyncio.Lock | None = PrivateAttr(default=None)
|
|
51
|
+
|
|
52
|
+
async def _ensure_responses_websocket(self) -> Any:
|
|
53
|
+
connection = self._responses_ws_connection
|
|
54
|
+
if connection is not None:
|
|
55
|
+
return connection
|
|
56
|
+
client = self.root_async_client
|
|
57
|
+
# ChatOpenAI accepts an async API-key callable so it can avoid creating an
|
|
58
|
+
# eager sync OpenAI client. HTTP requests refresh that callable in
|
|
59
|
+
# AsyncOpenAI._prepare_options(), but the SDK's responses.connect() path
|
|
60
|
+
# reads auth_headers directly. Refresh it here or the WebSocket handshake
|
|
61
|
+
# is sent without Authorization and OpenAI-compatible servers return 401.
|
|
62
|
+
refresh_api_key = getattr(client, "_refresh_api_key", None)
|
|
63
|
+
if callable(refresh_api_key):
|
|
64
|
+
await refresh_api_key()
|
|
65
|
+
# Some OpenAI-compatible gateways don't answer WebSocket ping frames while
|
|
66
|
+
# a model is reasoning. Keep sending pings to keep intermediaries awake,
|
|
67
|
+
# but don't terminate an otherwise healthy response solely for a missing
|
|
68
|
+
# pong (websockets defaults to a 20-second ping timeout).
|
|
69
|
+
manager = client.responses.connect(
|
|
70
|
+
websocket_connection_options={"ping_timeout": None}
|
|
71
|
+
)
|
|
72
|
+
connection = await manager.enter()
|
|
73
|
+
self._responses_ws_manager = manager
|
|
74
|
+
self._responses_ws_connection = connection
|
|
75
|
+
return connection
|
|
76
|
+
|
|
77
|
+
async def _reset_responses_websocket(self) -> None:
|
|
78
|
+
connection = self._responses_ws_connection
|
|
79
|
+
self._responses_ws_connection = None
|
|
80
|
+
self._responses_ws_manager = None
|
|
81
|
+
if connection is not None:
|
|
82
|
+
try:
|
|
83
|
+
await connection.close()
|
|
84
|
+
except Exception: # noqa: BLE001
|
|
85
|
+
pass
|
|
86
|
+
|
|
87
|
+
async def aclose(self) -> None:
|
|
88
|
+
"""Close the persistent Responses WebSocket, if it was opened."""
|
|
89
|
+
await self._reset_responses_websocket()
|
|
90
|
+
|
|
91
|
+
async def _recv_event(self, connection: Any) -> Any:
|
|
92
|
+
timeout = self.stream_chunk_timeout
|
|
93
|
+
if timeout is None or timeout <= 0:
|
|
94
|
+
return await connection.recv()
|
|
95
|
+
return await asyncio.wait_for(connection.recv(), timeout=float(timeout))
|
|
96
|
+
|
|
97
|
+
async def _astream(
|
|
98
|
+
self,
|
|
99
|
+
*args: Any,
|
|
100
|
+
**kwargs: Any,
|
|
101
|
+
) -> AsyncIterator[ChatGenerationChunk]:
|
|
102
|
+
"""Route LangChain streaming calls to the Responses WebSocket."""
|
|
103
|
+
async for chunk in self._astream_responses(*args, **kwargs):
|
|
104
|
+
yield chunk
|
|
105
|
+
|
|
106
|
+
async def _agenerate(
|
|
107
|
+
self,
|
|
108
|
+
messages: list[BaseMessage],
|
|
109
|
+
stop: list[str] | None = None,
|
|
110
|
+
run_manager: AsyncCallbackManagerForLLMRun | None = None,
|
|
111
|
+
**kwargs: Any,
|
|
112
|
+
) -> ChatResult:
|
|
113
|
+
"""Make non-streaming LangChain calls use the same WebSocket transport."""
|
|
114
|
+
return await agenerate_from_stream(
|
|
115
|
+
self._astream_responses(
|
|
116
|
+
messages,
|
|
117
|
+
stop=stop,
|
|
118
|
+
run_manager=run_manager,
|
|
119
|
+
**kwargs,
|
|
120
|
+
)
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
async def _astream_responses(
|
|
124
|
+
self,
|
|
125
|
+
messages: list[BaseMessage],
|
|
126
|
+
stop: list[str] | None = None,
|
|
127
|
+
run_manager: AsyncCallbackManagerForLLMRun | None = None,
|
|
128
|
+
**kwargs: Any,
|
|
129
|
+
) -> AsyncIterator[ChatGenerationChunk]:
|
|
130
|
+
"""Stream one response over the model's persistent WebSocket."""
|
|
131
|
+
kwargs["stream"] = True
|
|
132
|
+
payload = self._get_request_payload(messages, stop=stop, **kwargs)
|
|
133
|
+
event = prepare_responses_websocket_event(payload)
|
|
134
|
+
|
|
135
|
+
if self._responses_ws_lock is None:
|
|
136
|
+
self._responses_ws_lock = asyncio.Lock()
|
|
137
|
+
|
|
138
|
+
async with self._responses_ws_lock:
|
|
139
|
+
connection = await self._ensure_responses_websocket()
|
|
140
|
+
try:
|
|
141
|
+
await connection.send(event)
|
|
142
|
+
current_index = -1
|
|
143
|
+
current_output_index = -1
|
|
144
|
+
current_sub_index = -1
|
|
145
|
+
has_reasoning = False
|
|
146
|
+
original_schema_obj = kwargs.get("response_format")
|
|
147
|
+
|
|
148
|
+
while True:
|
|
149
|
+
response_event = await self._recv_event(connection)
|
|
150
|
+
event_type = str(getattr(response_event, "type", ""))
|
|
151
|
+
if event_type == "error":
|
|
152
|
+
error = getattr(response_event, "error", None)
|
|
153
|
+
message = getattr(error, "message", None) or str(error or response_event)
|
|
154
|
+
raise RuntimeError(f"Responses WebSocket error: {message}")
|
|
155
|
+
|
|
156
|
+
(
|
|
157
|
+
current_index,
|
|
158
|
+
current_output_index,
|
|
159
|
+
current_sub_index,
|
|
160
|
+
generation_chunk,
|
|
161
|
+
) = _convert_responses_chunk_to_generation_chunk(
|
|
162
|
+
response_event,
|
|
163
|
+
current_index,
|
|
164
|
+
current_output_index,
|
|
165
|
+
current_sub_index,
|
|
166
|
+
schema=original_schema_obj,
|
|
167
|
+
metadata={},
|
|
168
|
+
has_reasoning=has_reasoning,
|
|
169
|
+
output_version=self.output_version,
|
|
170
|
+
)
|
|
171
|
+
if generation_chunk is not None:
|
|
172
|
+
if run_manager is not None:
|
|
173
|
+
await run_manager.on_llm_new_token(
|
|
174
|
+
generation_chunk.text,
|
|
175
|
+
chunk=generation_chunk,
|
|
176
|
+
)
|
|
177
|
+
if "reasoning" in generation_chunk.message.additional_kwargs:
|
|
178
|
+
has_reasoning = True
|
|
179
|
+
yield generation_chunk
|
|
180
|
+
|
|
181
|
+
if event_type in _TERMINAL_EVENT_TYPES:
|
|
182
|
+
break
|
|
183
|
+
except BaseException:
|
|
184
|
+
# Cancellation or a protocol error may leave unread events on the
|
|
185
|
+
# socket. Reopen it before the next model turn.
|
|
186
|
+
await self._reset_responses_websocket()
|
|
187
|
+
raise
|