devcopilot 0.2.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.
- api/__init__.py +17 -0
- api/admin_config.py +1303 -0
- api/admin_routes.py +287 -0
- api/admin_static/admin.css +459 -0
- api/admin_static/admin.js +497 -0
- api/admin_static/index.html +77 -0
- api/admin_urls.py +34 -0
- api/app.py +194 -0
- api/command_utils.py +164 -0
- api/dependencies.py +144 -0
- api/detection.py +152 -0
- api/gateway_model_ids.py +54 -0
- api/model_catalog.py +133 -0
- api/model_router.py +125 -0
- api/models/__init__.py +45 -0
- api/models/anthropic.py +234 -0
- api/models/openai_responses.py +28 -0
- api/models/responses.py +60 -0
- api/optimization_handlers.py +154 -0
- api/request_pipeline.py +424 -0
- api/routes.py +156 -0
- api/runtime.py +334 -0
- api/validation_log.py +48 -0
- api/web_server_tools.py +22 -0
- api/web_tools/__init__.py +17 -0
- api/web_tools/constants.py +15 -0
- api/web_tools/egress.py +99 -0
- api/web_tools/outbound.py +278 -0
- api/web_tools/parsers.py +104 -0
- api/web_tools/request.py +87 -0
- api/web_tools/streaming.py +206 -0
- cli/__init__.py +5 -0
- cli/claude_env.py +12 -0
- cli/entrypoints.py +166 -0
- cli/env.example +209 -0
- cli/launchers/__init__.py +1 -0
- cli/launchers/claude.py +84 -0
- cli/launchers/codex.py +204 -0
- cli/launchers/codex_model_catalog.py +186 -0
- cli/launchers/common.py +93 -0
- cli/managed/__init__.py +6 -0
- cli/managed/claude.py +215 -0
- cli/managed/manager.py +157 -0
- cli/managed/session.py +260 -0
- cli/process_registry.py +78 -0
- config/__init__.py +5 -0
- config/constants.py +13 -0
- config/logging_config.py +159 -0
- config/nim.py +118 -0
- config/paths.py +91 -0
- config/provider_catalog.py +259 -0
- config/provider_ids.py +7 -0
- config/settings.py +538 -0
- core/__init__.py +1 -0
- core/anthropic/__init__.py +46 -0
- core/anthropic/content.py +31 -0
- core/anthropic/conversion.py +587 -0
- core/anthropic/emitted_sse_tracker.py +346 -0
- core/anthropic/errors.py +70 -0
- core/anthropic/native_messages_request.py +280 -0
- core/anthropic/native_sse_block_policy.py +313 -0
- core/anthropic/provider_stream_error.py +34 -0
- core/anthropic/server_tool_sse.py +14 -0
- core/anthropic/sse.py +440 -0
- core/anthropic/stream_contracts.py +205 -0
- core/anthropic/stream_recovery.py +346 -0
- core/anthropic/stream_recovery_session.py +133 -0
- core/anthropic/thinking.py +140 -0
- core/anthropic/tokens.py +117 -0
- core/anthropic/tools.py +212 -0
- core/anthropic/utils.py +9 -0
- core/openai_responses/__init__.py +5 -0
- core/openai_responses/adapter.py +31 -0
- core/openai_responses/anthropic_sse.py +59 -0
- core/openai_responses/errors.py +22 -0
- core/openai_responses/events.py +19 -0
- core/openai_responses/ids.py +21 -0
- core/openai_responses/input.py +258 -0
- core/openai_responses/items.py +37 -0
- core/openai_responses/reasoning.py +52 -0
- core/openai_responses/stream.py +25 -0
- core/openai_responses/stream_state.py +654 -0
- core/openai_responses/tools.py +374 -0
- core/openai_responses/usage.py +37 -0
- core/rate_limit.py +60 -0
- core/trace.py +216 -0
- devcopilot-0.2.0.dist-info/METADATA +687 -0
- devcopilot-0.2.0.dist-info/RECORD +189 -0
- devcopilot-0.2.0.dist-info/WHEEL +4 -0
- devcopilot-0.2.0.dist-info/entry_points.txt +6 -0
- devcopilot-0.2.0.dist-info/licenses/LICENSE +21 -0
- messaging/__init__.py +26 -0
- messaging/cli_event_constants.py +67 -0
- messaging/command_context.py +66 -0
- messaging/command_dispatcher.py +37 -0
- messaging/commands.py +275 -0
- messaging/event_parser.py +181 -0
- messaging/limiter.py +300 -0
- messaging/models.py +36 -0
- messaging/node_event_pipeline.py +127 -0
- messaging/node_runner.py +342 -0
- messaging/platforms/__init__.py +15 -0
- messaging/platforms/base.py +228 -0
- messaging/platforms/discord.py +567 -0
- messaging/platforms/factory.py +103 -0
- messaging/platforms/outbox.py +144 -0
- messaging/platforms/telegram.py +688 -0
- messaging/platforms/voice_flow.py +295 -0
- messaging/rendering/__init__.py +3 -0
- messaging/rendering/discord_markdown.py +318 -0
- messaging/rendering/markdown_tables.py +49 -0
- messaging/rendering/profiles.py +55 -0
- messaging/rendering/telegram_markdown.py +327 -0
- messaging/safe_diagnostics.py +17 -0
- messaging/session.py +334 -0
- messaging/transcript.py +581 -0
- messaging/transcription.py +164 -0
- messaging/trees/__init__.py +15 -0
- messaging/trees/data.py +482 -0
- messaging/trees/manager.py +433 -0
- messaging/trees/processor.py +179 -0
- messaging/trees/repository.py +177 -0
- messaging/turn_intake.py +235 -0
- messaging/ui_updates.py +101 -0
- messaging/voice.py +76 -0
- messaging/workflow.py +200 -0
- providers/__init__.py +31 -0
- providers/base.py +152 -0
- providers/cerebras/__init__.py +7 -0
- providers/cerebras/client.py +31 -0
- providers/cerebras/request.py +55 -0
- providers/codestral/__init__.py +7 -0
- providers/codestral/client.py +34 -0
- providers/deepseek/__init__.py +11 -0
- providers/deepseek/client.py +51 -0
- providers/deepseek/request.py +475 -0
- providers/defaults.py +41 -0
- providers/error_mapping.py +309 -0
- providers/exceptions.py +113 -0
- providers/fireworks/__init__.py +5 -0
- providers/fireworks/client.py +45 -0
- providers/fireworks/request.py +48 -0
- providers/gemini/__init__.py +7 -0
- providers/gemini/client.py +49 -0
- providers/gemini/request.py +199 -0
- providers/groq/__init__.py +7 -0
- providers/groq/client.py +31 -0
- providers/groq/request.py +83 -0
- providers/kimi/__init__.py +10 -0
- providers/kimi/client.py +53 -0
- providers/kimi/request.py +42 -0
- providers/llamacpp/__init__.py +3 -0
- providers/llamacpp/client.py +16 -0
- providers/lmstudio/__init__.py +5 -0
- providers/lmstudio/client.py +16 -0
- providers/mistral/__init__.py +7 -0
- providers/mistral/client.py +31 -0
- providers/mistral/request.py +37 -0
- providers/model_listing.py +133 -0
- providers/nvidia_nim/__init__.py +7 -0
- providers/nvidia_nim/client.py +91 -0
- providers/nvidia_nim/request.py +430 -0
- providers/nvidia_nim/voice.py +95 -0
- providers/ollama/__init__.py +7 -0
- providers/ollama/client.py +39 -0
- providers/open_router/__init__.py +7 -0
- providers/open_router/client.py +124 -0
- providers/open_router/request.py +42 -0
- providers/opencode/__init__.py +11 -0
- providers/opencode/client.py +31 -0
- providers/opencode/request.py +35 -0
- providers/rate_limit.py +300 -0
- providers/registry.py +527 -0
- providers/transports/__init__.py +1 -0
- providers/transports/anthropic_messages/__init__.py +5 -0
- providers/transports/anthropic_messages/http.py +118 -0
- providers/transports/anthropic_messages/recovery.py +206 -0
- providers/transports/anthropic_messages/stream.py +295 -0
- providers/transports/anthropic_messages/transport.py +236 -0
- providers/transports/openai_chat/__init__.py +5 -0
- providers/transports/openai_chat/recovery.py +217 -0
- providers/transports/openai_chat/stream.py +384 -0
- providers/transports/openai_chat/tool_calls.py +293 -0
- providers/transports/openai_chat/transport.py +156 -0
- providers/wafer/__init__.py +10 -0
- providers/wafer/client.py +50 -0
- providers/zai/__init__.py +10 -0
- providers/zai/client.py +46 -0
- providers/zai/request.py +42 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"""OpenAI-compatible chat transport base."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from abc import abstractmethod
|
|
6
|
+
from collections.abc import AsyncIterator, Iterator
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
import httpx
|
|
10
|
+
from openai import AsyncOpenAI
|
|
11
|
+
|
|
12
|
+
from core.anthropic import SSEBuilder
|
|
13
|
+
from providers.base import BaseProvider, ProviderConfig
|
|
14
|
+
from providers.error_mapping import (
|
|
15
|
+
extract_provider_error_detail,
|
|
16
|
+
map_error,
|
|
17
|
+
user_visible_message_for_mapped_provider_error,
|
|
18
|
+
)
|
|
19
|
+
from providers.model_listing import extract_openai_model_ids
|
|
20
|
+
from providers.rate_limit import GlobalRateLimiter
|
|
21
|
+
|
|
22
|
+
from .stream import OpenAIChatStreamRunner
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class OpenAIChatTransport(BaseProvider):
|
|
26
|
+
"""Base for OpenAI-compatible ``/chat/completions`` adapters."""
|
|
27
|
+
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
config: ProviderConfig,
|
|
31
|
+
*,
|
|
32
|
+
provider_name: str,
|
|
33
|
+
base_url: str,
|
|
34
|
+
api_key: str,
|
|
35
|
+
):
|
|
36
|
+
super().__init__(config)
|
|
37
|
+
self._provider_name = provider_name
|
|
38
|
+
self._api_key = api_key
|
|
39
|
+
self._base_url = base_url.rstrip("/")
|
|
40
|
+
self._global_rate_limiter = GlobalRateLimiter.get_scoped_instance(
|
|
41
|
+
provider_name.lower(),
|
|
42
|
+
rate_limit=config.rate_limit,
|
|
43
|
+
rate_window=config.rate_window,
|
|
44
|
+
max_concurrency=config.max_concurrency,
|
|
45
|
+
)
|
|
46
|
+
http_client = None
|
|
47
|
+
if config.proxy:
|
|
48
|
+
http_client = httpx.AsyncClient(
|
|
49
|
+
proxy=config.proxy,
|
|
50
|
+
timeout=httpx.Timeout(
|
|
51
|
+
config.http_read_timeout,
|
|
52
|
+
connect=config.http_connect_timeout,
|
|
53
|
+
read=config.http_read_timeout,
|
|
54
|
+
write=config.http_write_timeout,
|
|
55
|
+
),
|
|
56
|
+
)
|
|
57
|
+
self._client = AsyncOpenAI(
|
|
58
|
+
api_key=self._api_key,
|
|
59
|
+
base_url=self._base_url,
|
|
60
|
+
max_retries=0,
|
|
61
|
+
timeout=httpx.Timeout(
|
|
62
|
+
config.http_read_timeout,
|
|
63
|
+
connect=config.http_connect_timeout,
|
|
64
|
+
read=config.http_read_timeout,
|
|
65
|
+
write=config.http_write_timeout,
|
|
66
|
+
),
|
|
67
|
+
http_client=http_client,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
async def cleanup(self) -> None:
|
|
71
|
+
"""Release HTTP client resources."""
|
|
72
|
+
client = getattr(self, "_client", None)
|
|
73
|
+
if client is not None:
|
|
74
|
+
await client.close()
|
|
75
|
+
|
|
76
|
+
async def list_model_ids(self) -> frozenset[str]:
|
|
77
|
+
"""Return model ids from the provider's OpenAI-compatible models endpoint."""
|
|
78
|
+
payload = await self._client.models.list()
|
|
79
|
+
return extract_openai_model_ids(payload, provider_name=self._provider_name)
|
|
80
|
+
|
|
81
|
+
@abstractmethod
|
|
82
|
+
def _build_request_body(
|
|
83
|
+
self, request: Any, thinking_enabled: bool | None = None
|
|
84
|
+
) -> dict:
|
|
85
|
+
"""Build request body. Must be implemented by subclasses."""
|
|
86
|
+
|
|
87
|
+
def _handle_extra_reasoning(
|
|
88
|
+
self, delta: Any, sse: SSEBuilder, *, thinking_enabled: bool
|
|
89
|
+
) -> Iterator[str]:
|
|
90
|
+
"""Hook for provider-specific reasoning."""
|
|
91
|
+
return iter(())
|
|
92
|
+
|
|
93
|
+
def _get_retry_request_body(self, error: Exception, body: dict) -> dict | None:
|
|
94
|
+
"""Return a modified request body for one retry, or None."""
|
|
95
|
+
return None
|
|
96
|
+
|
|
97
|
+
def _prepare_create_body(self, body: dict[str, Any]) -> dict[str, Any]:
|
|
98
|
+
"""Return the body passed to the upstream OpenAI-compatible client."""
|
|
99
|
+
return body
|
|
100
|
+
|
|
101
|
+
def _record_tool_call_extra_content(
|
|
102
|
+
self, tool_call_id: str, extra_content: dict[str, Any]
|
|
103
|
+
) -> None:
|
|
104
|
+
"""Hook for providers that must replay OpenAI tool-call metadata later."""
|
|
105
|
+
|
|
106
|
+
def _tool_argument_aliases(self, body: dict[str, Any]) -> dict[str, dict[str, str]]:
|
|
107
|
+
"""Return provider-specific per-tool argument aliases for this request."""
|
|
108
|
+
return {}
|
|
109
|
+
|
|
110
|
+
async def _create_stream(self, body: dict) -> tuple[Any, dict]:
|
|
111
|
+
"""Create a streaming chat completion, optionally retrying once."""
|
|
112
|
+
try:
|
|
113
|
+
create_body = self._prepare_create_body(body)
|
|
114
|
+
stream = await self._global_rate_limiter.execute_with_retry(
|
|
115
|
+
self._client.chat.completions.create, **create_body, stream=True
|
|
116
|
+
)
|
|
117
|
+
return stream, body
|
|
118
|
+
except Exception as error:
|
|
119
|
+
retry_body = self._get_retry_request_body(error, body)
|
|
120
|
+
if retry_body is None:
|
|
121
|
+
raise
|
|
122
|
+
|
|
123
|
+
create_retry_body = self._prepare_create_body(retry_body)
|
|
124
|
+
stream = await self._global_rate_limiter.execute_with_retry(
|
|
125
|
+
self._client.chat.completions.create, **create_retry_body, stream=True
|
|
126
|
+
)
|
|
127
|
+
return stream, retry_body
|
|
128
|
+
|
|
129
|
+
def _openai_error_message(self, error: Exception, request_id: str | None) -> str:
|
|
130
|
+
mapped_error = map_error(error, rate_limiter=self._global_rate_limiter)
|
|
131
|
+
return user_visible_message_for_mapped_provider_error(
|
|
132
|
+
mapped_error,
|
|
133
|
+
provider_name=self._provider_name,
|
|
134
|
+
read_timeout_s=self._config.http_read_timeout,
|
|
135
|
+
detail=extract_provider_error_detail(error),
|
|
136
|
+
request_id=request_id,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
async def stream_response(
|
|
140
|
+
self,
|
|
141
|
+
request: Any,
|
|
142
|
+
input_tokens: int = 0,
|
|
143
|
+
*,
|
|
144
|
+
request_id: str | None = None,
|
|
145
|
+
thinking_enabled: bool | None = None,
|
|
146
|
+
) -> AsyncIterator[str]:
|
|
147
|
+
"""Stream response in Anthropic SSE format."""
|
|
148
|
+
runner = OpenAIChatStreamRunner(
|
|
149
|
+
self,
|
|
150
|
+
request=request,
|
|
151
|
+
input_tokens=input_tokens,
|
|
152
|
+
request_id=request_id,
|
|
153
|
+
thinking_enabled=thinking_enabled,
|
|
154
|
+
)
|
|
155
|
+
async for event in runner.run():
|
|
156
|
+
yield event
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Wafer provider implementation (native Anthropic-compatible Messages)."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from providers.base import ProviderConfig
|
|
6
|
+
from providers.defaults import WAFER_DEFAULT_BASE
|
|
7
|
+
from providers.transports.anthropic_messages import AnthropicMessagesTransport
|
|
8
|
+
|
|
9
|
+
_ANTHROPIC_VERSION = "2023-06-01"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class WaferProvider(AnthropicMessagesTransport):
|
|
13
|
+
"""Wafer using ``https://pass.wafer.ai/v1/messages``."""
|
|
14
|
+
|
|
15
|
+
def __init__(self, config: ProviderConfig):
|
|
16
|
+
super().__init__(
|
|
17
|
+
config,
|
|
18
|
+
provider_name="WAFER",
|
|
19
|
+
default_base_url=WAFER_DEFAULT_BASE,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
def _build_request_body(
|
|
23
|
+
self, request: Any, thinking_enabled: bool | None = None
|
|
24
|
+
) -> dict:
|
|
25
|
+
"""Build native body; Wafer rejects omitted thinking as ``reasoning_effort=none``."""
|
|
26
|
+
effective_thinking_enabled = self._is_thinking_enabled(
|
|
27
|
+
request, thinking_enabled
|
|
28
|
+
)
|
|
29
|
+
body = self._build_request_body_with_resolved_thinking(
|
|
30
|
+
request,
|
|
31
|
+
thinking_enabled=effective_thinking_enabled,
|
|
32
|
+
)
|
|
33
|
+
if "thinking" not in body:
|
|
34
|
+
body["thinking"] = (
|
|
35
|
+
{"type": "enabled"}
|
|
36
|
+
if effective_thinking_enabled
|
|
37
|
+
else {"type": "disabled"}
|
|
38
|
+
)
|
|
39
|
+
return body
|
|
40
|
+
|
|
41
|
+
def _request_headers(self) -> dict[str, str]:
|
|
42
|
+
return {
|
|
43
|
+
"Accept": "text/event-stream",
|
|
44
|
+
"Authorization": f"Bearer {self._api_key}",
|
|
45
|
+
"Content-Type": "application/json",
|
|
46
|
+
"anthropic-version": _ANTHROPIC_VERSION,
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
def _model_list_headers(self) -> dict[str, str]:
|
|
50
|
+
return {"Authorization": f"Bearer {self._api_key}"}
|
providers/zai/client.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Z.ai provider implementation (Anthropic-compatible Messages API)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from providers.base import ProviderConfig
|
|
8
|
+
from providers.defaults import ZAI_DEFAULT_BASE
|
|
9
|
+
from providers.transports.anthropic_messages import AnthropicMessagesTransport
|
|
10
|
+
|
|
11
|
+
from .request import build_request_body
|
|
12
|
+
|
|
13
|
+
_ANTHROPIC_VERSION = "2023-06-01"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ZaiProvider(AnthropicMessagesTransport):
|
|
17
|
+
"""Z.ai using Anthropic-compatible Messages at api.z.ai/api/anthropic/v1."""
|
|
18
|
+
|
|
19
|
+
def __init__(self, config: ProviderConfig):
|
|
20
|
+
super().__init__(
|
|
21
|
+
config,
|
|
22
|
+
provider_name="ZAI",
|
|
23
|
+
default_base_url=ZAI_DEFAULT_BASE,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
def _build_request_body(
|
|
27
|
+
self, request: Any, thinking_enabled: bool | None = None
|
|
28
|
+
) -> dict:
|
|
29
|
+
return build_request_body(
|
|
30
|
+
request,
|
|
31
|
+
thinking_enabled=self._is_thinking_enabled(request, thinking_enabled),
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
def _request_headers(self) -> dict[str, str]:
|
|
35
|
+
return {
|
|
36
|
+
"Accept": "text/event-stream",
|
|
37
|
+
"Content-Type": "application/json",
|
|
38
|
+
"x-api-key": self._api_key,
|
|
39
|
+
"anthropic-version": _ANTHROPIC_VERSION,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
def _model_list_headers(self) -> dict[str, str]:
|
|
43
|
+
return {
|
|
44
|
+
"x-api-key": self._api_key,
|
|
45
|
+
"anthropic-version": _ANTHROPIC_VERSION,
|
|
46
|
+
}
|
providers/zai/request.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Native Anthropic Messages request builder for Z.ai."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from loguru import logger
|
|
8
|
+
|
|
9
|
+
from config.constants import ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS
|
|
10
|
+
from core.anthropic.native_messages_request import (
|
|
11
|
+
build_base_native_anthropic_request_body,
|
|
12
|
+
)
|
|
13
|
+
from providers.exceptions import InvalidRequestError
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def build_request_body(request_data: Any, *, thinking_enabled: bool) -> dict:
|
|
17
|
+
"""Build JSON for Z.ai Anthropic-compat ``POST …/messages``."""
|
|
18
|
+
logger.debug(
|
|
19
|
+
"ZAI_REQUEST: native build model={} msgs={}",
|
|
20
|
+
getattr(request_data, "model", "?"),
|
|
21
|
+
len(getattr(request_data, "messages", [])),
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
body = build_base_native_anthropic_request_body(
|
|
25
|
+
request_data,
|
|
26
|
+
default_max_tokens=ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS,
|
|
27
|
+
thinking_enabled=thinking_enabled,
|
|
28
|
+
)
|
|
29
|
+
extra = getattr(request_data, "extra_body", None)
|
|
30
|
+
if extra:
|
|
31
|
+
raise InvalidRequestError(
|
|
32
|
+
"Z.ai native Messages API does not support extra_body on requests."
|
|
33
|
+
)
|
|
34
|
+
body["stream"] = True
|
|
35
|
+
|
|
36
|
+
logger.debug(
|
|
37
|
+
"ZAI_REQUEST: build done model={} msgs={} tools={}",
|
|
38
|
+
body.get("model"),
|
|
39
|
+
len(body.get("messages", [])),
|
|
40
|
+
len(body.get("tools", [])),
|
|
41
|
+
)
|
|
42
|
+
return body
|