codeer-cli 0.1.9__py3-none-any.whl → 0.1.11__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.
- codeer_cli/__init__.py +2 -1
- codeer_cli/_validate.py +42 -1
- codeer_cli/agents.py +9 -1
- codeer_cli/chats.py +136 -22
- codeer_cli/cli.py +8 -0
- codeer_cli/client.py +30 -5
- codeer_cli/commands/agent.py +26 -3
- codeer_cli/commands/history.py +26 -12
- codeer_cli/commands/model.py +43 -0
- codeer_cli/models.py +16 -0
- codeer_cli/parse.py +7 -8
- {codeer_cli-0.1.9.dist-info → codeer_cli-0.1.11.dist-info}/METADATA +37 -6
- codeer_cli-0.1.11.dist-info/RECORD +25 -0
- codeer_cli-0.1.9.dist-info/RECORD +0 -23
- {codeer_cli-0.1.9.dist-info → codeer_cli-0.1.11.dist-info}/WHEEL +0 -0
- {codeer_cli-0.1.9.dist-info → codeer_cli-0.1.11.dist-info}/entry_points.txt +0 -0
codeer_cli/__init__.py
CHANGED
|
@@ -18,7 +18,7 @@ production and can be overridden from process env. The CLI does not read
|
|
|
18
18
|
workspace-local dotenv files or credential files.
|
|
19
19
|
"""
|
|
20
20
|
|
|
21
|
-
from ._validate import ToolValidationError
|
|
21
|
+
from ._validate import HumanHandoffValidationError, ToolValidationError
|
|
22
22
|
from .client import AuthError, CodeerClient, CodeerError, TransportError
|
|
23
23
|
from .parse import (
|
|
24
24
|
AgentSummary,
|
|
@@ -44,6 +44,7 @@ from .parse import (
|
|
|
44
44
|
|
|
45
45
|
__all__ = [
|
|
46
46
|
"CodeerClient", "CodeerError", "TransportError", "AuthError", "ToolValidationError",
|
|
47
|
+
"HumanHandoffValidationError",
|
|
47
48
|
# parsers
|
|
48
49
|
"AgentSummary", "ConversationTurn", "EvalResultSummary", "HistorySummary",
|
|
49
50
|
"KBNode", "ToolCall", "EvalToolCall",
|
codeer_cli/_validate.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Client-side validation for
|
|
1
|
+
"""Client-side validation for agent payloads.
|
|
2
2
|
|
|
3
3
|
These checks exist because the backend's form-schema validator is lenient
|
|
4
4
|
(``extra="allow"``) and silently accepts unknown ``type`` strings, which then
|
|
@@ -24,6 +24,47 @@ class ToolValidationError(ValueError):
|
|
|
24
24
|
"""Raised when a unified_tools payload is definitely wrong."""
|
|
25
25
|
|
|
26
26
|
|
|
27
|
+
class HumanHandoffValidationError(ValueError):
|
|
28
|
+
"""Raised when a human_handoff payload is definitely wrong."""
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def validate_human_handoff(config: Any) -> dict[str, Any] | None:
|
|
32
|
+
"""Validate and normalize an optional human-handoff configuration."""
|
|
33
|
+
if config is None:
|
|
34
|
+
return None
|
|
35
|
+
if not isinstance(config, dict):
|
|
36
|
+
raise HumanHandoffValidationError("human_handoff must be an object.")
|
|
37
|
+
|
|
38
|
+
allowed_keys = {"enabled", "idle_timeout_minutes", "handoff_instructions"}
|
|
39
|
+
unknown_keys = sorted(set(config) - allowed_keys)
|
|
40
|
+
if unknown_keys:
|
|
41
|
+
raise HumanHandoffValidationError(
|
|
42
|
+
f"human_handoff contains unsupported field(s): {', '.join(unknown_keys)}."
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
enabled = config.get("enabled", False)
|
|
46
|
+
if not isinstance(enabled, bool):
|
|
47
|
+
raise HumanHandoffValidationError("human_handoff.enabled must be true or false.")
|
|
48
|
+
|
|
49
|
+
timeout = config.get("idle_timeout_minutes")
|
|
50
|
+
if timeout is not None and (type(timeout) is not int or timeout <= 0):
|
|
51
|
+
raise HumanHandoffValidationError(
|
|
52
|
+
"human_handoff.idle_timeout_minutes must be a positive integer or null."
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
instructions = config.get("handoff_instructions")
|
|
56
|
+
if instructions is not None and not isinstance(instructions, str):
|
|
57
|
+
raise HumanHandoffValidationError(
|
|
58
|
+
"human_handoff.handoff_instructions must be a string or null."
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
"enabled": enabled,
|
|
63
|
+
"idle_timeout_minutes": timeout,
|
|
64
|
+
"handoff_instructions": instructions,
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
27
68
|
def validate_unified_tools(tools: Iterable[dict[str, Any]] | None) -> list[dict[str, Any]]:
|
|
28
69
|
"""Raise ToolValidationError on invalid payloads; otherwise return the list.
|
|
29
70
|
|
codeer_cli/agents.py
CHANGED
|
@@ -7,7 +7,7 @@ from __future__ import annotations
|
|
|
7
7
|
|
|
8
8
|
from typing import Any, List, Optional
|
|
9
9
|
|
|
10
|
-
from ._validate import validate_unified_tools
|
|
10
|
+
from ._validate import validate_human_handoff, validate_unified_tools
|
|
11
11
|
from .client import CodeerClient
|
|
12
12
|
|
|
13
13
|
|
|
@@ -24,8 +24,10 @@ def create(
|
|
|
24
24
|
suggested_questions: Optional[List[str]] = None,
|
|
25
25
|
primary_object_ids: Optional[List[int]] = None,
|
|
26
26
|
attachment_ids: Optional[List[str]] = None,
|
|
27
|
+
human_handoff: Optional[dict[str, Any]] = None,
|
|
27
28
|
) -> dict:
|
|
28
29
|
validated_tools = validate_unified_tools(unified_tools)
|
|
30
|
+
validated_handoff = validate_human_handoff(human_handoff)
|
|
29
31
|
body: dict[str, Any] = {
|
|
30
32
|
"name": name,
|
|
31
33
|
"system_prompt": system_prompt,
|
|
@@ -39,6 +41,8 @@ def create(
|
|
|
39
41
|
body["description"] = description
|
|
40
42
|
if llm_model is not None:
|
|
41
43
|
body["llm_model"] = llm_model
|
|
44
|
+
if validated_handoff is not None:
|
|
45
|
+
body["human_handoff"] = validated_handoff
|
|
42
46
|
return client.post("/external/agents", json=body)
|
|
43
47
|
|
|
44
48
|
|
|
@@ -56,9 +60,11 @@ def update(
|
|
|
56
60
|
suggested_questions: Optional[List[str]] = None,
|
|
57
61
|
primary_object_ids: Optional[List[int]] = None,
|
|
58
62
|
attachment_ids: Optional[List[str]] = None,
|
|
63
|
+
human_handoff: Optional[dict[str, Any]] = None,
|
|
59
64
|
) -> dict:
|
|
60
65
|
"""PUT creates a new AgentHistory snapshot (draft)."""
|
|
61
66
|
validated_tools = validate_unified_tools(unified_tools)
|
|
67
|
+
validated_handoff = validate_human_handoff(human_handoff)
|
|
62
68
|
body: dict[str, Any] = {
|
|
63
69
|
"name": name,
|
|
64
70
|
"system_prompt": system_prompt,
|
|
@@ -73,6 +79,8 @@ def update(
|
|
|
73
79
|
body["description"] = description
|
|
74
80
|
if llm_model is not None:
|
|
75
81
|
body["llm_model"] = llm_model
|
|
82
|
+
if validated_handoff is not None:
|
|
83
|
+
body["human_handoff"] = validated_handoff
|
|
76
84
|
return client.patch(f"/external/agents/{agent_id}", json=body)
|
|
77
85
|
|
|
78
86
|
|
codeer_cli/chats.py
CHANGED
|
@@ -1,15 +1,10 @@
|
|
|
1
|
-
"""Chat creation
|
|
2
|
-
|
|
3
|
-
The send_message() path is the main dogfood driver: open a chat against a
|
|
4
|
-
specific agent version (unpublished draft is fine) and consume the SSE stream
|
|
5
|
-
to get tool calls, reasoning, and final text back.
|
|
6
|
-
"""
|
|
1
|
+
"""Chat V2 creation, structured SSE responses, and persisted message reads."""
|
|
7
2
|
|
|
8
3
|
from __future__ import annotations
|
|
9
4
|
|
|
10
5
|
from typing import Any, Iterator, List, Optional
|
|
11
6
|
|
|
12
|
-
from .client import CodeerClient
|
|
7
|
+
from .client import CodeerClient, TransportError
|
|
13
8
|
|
|
14
9
|
|
|
15
10
|
def create(
|
|
@@ -19,12 +14,13 @@ def create(
|
|
|
19
14
|
title: Optional[str] = None,
|
|
20
15
|
external_user_id: Optional[str] = None,
|
|
21
16
|
) -> dict:
|
|
22
|
-
body: dict[str, Any] = {
|
|
23
|
-
|
|
24
|
-
|
|
17
|
+
body: dict[str, Any] = {
|
|
18
|
+
"agent_id": agent_id,
|
|
19
|
+
"name": title or "CLI conversation",
|
|
20
|
+
}
|
|
25
21
|
if external_user_id is not None:
|
|
26
22
|
body["external_user_id"] = external_user_id
|
|
27
|
-
return client.post("/chats", json=body)
|
|
23
|
+
return client.post("/chats", api_version="v2", json=body)
|
|
28
24
|
|
|
29
25
|
|
|
30
26
|
def send_published_agent_message(
|
|
@@ -35,7 +31,7 @@ def send_published_agent_message(
|
|
|
35
31
|
agent_id: str,
|
|
36
32
|
external_user_id: Optional[str] = None,
|
|
37
33
|
attachment_ids: Optional[List[str]] = None,
|
|
38
|
-
stream: bool =
|
|
34
|
+
stream: bool = True,
|
|
39
35
|
timeout: Optional[float] = None,
|
|
40
36
|
) -> Iterator[dict] | dict:
|
|
41
37
|
"""Send a user message through the API-key external chat flow.
|
|
@@ -51,8 +47,14 @@ def send_published_agent_message(
|
|
|
51
47
|
|
|
52
48
|
path = f"/chats/{chat_id}/messages"
|
|
53
49
|
if stream:
|
|
54
|
-
return client.stream_sse(
|
|
55
|
-
|
|
50
|
+
return client.stream_sse(
|
|
51
|
+
"POST",
|
|
52
|
+
path,
|
|
53
|
+
api_version="v2",
|
|
54
|
+
json=body,
|
|
55
|
+
timeout=timeout,
|
|
56
|
+
)
|
|
57
|
+
return client.post(path, api_version="v2", json=body, timeout=timeout)
|
|
56
58
|
|
|
57
59
|
|
|
58
60
|
def send_message(
|
|
@@ -63,25 +65,137 @@ def send_message(
|
|
|
63
65
|
agent_history_id: str,
|
|
64
66
|
attachment_ids: Optional[List[str]] = None,
|
|
65
67
|
stream: bool = True,
|
|
68
|
+
timeout: Optional[float] = None,
|
|
66
69
|
) -> Iterator[dict] | dict:
|
|
67
|
-
"""
|
|
70
|
+
"""Use legacy Chat V1 to pin an unpublished agent version.
|
|
68
71
|
|
|
69
|
-
|
|
70
|
-
|
|
72
|
+
Chat V2's API-key external flow only accepts the published ``agent_id``.
|
|
73
|
+
Keep this low-level compatibility helper on V1 until V2 supports external
|
|
74
|
+
``agent_history_id`` pinning. It remains streaming by default.
|
|
71
75
|
"""
|
|
72
|
-
body: dict[str, Any] = {
|
|
76
|
+
body: dict[str, Any] = {
|
|
77
|
+
"message": message,
|
|
78
|
+
"agent_history_id": agent_history_id,
|
|
79
|
+
"stream": stream,
|
|
80
|
+
}
|
|
73
81
|
if attachment_ids:
|
|
74
82
|
body["attachment_ids"] = attachment_ids
|
|
75
83
|
|
|
76
84
|
path = f"/chats/{chat_id}/messages"
|
|
77
85
|
if stream:
|
|
78
|
-
return client.stream_sse(
|
|
79
|
-
|
|
86
|
+
return client.stream_sse(
|
|
87
|
+
"POST",
|
|
88
|
+
path,
|
|
89
|
+
json=body,
|
|
90
|
+
timeout=timeout,
|
|
91
|
+
)
|
|
92
|
+
return client.post(path, json=body, timeout=timeout)
|
|
80
93
|
|
|
81
94
|
|
|
82
|
-
def
|
|
83
|
-
|
|
95
|
+
def collect_stream(events: Iterator[dict]) -> dict:
|
|
96
|
+
"""Collect a Chat V2 SSE stream and require an explicit completion event."""
|
|
97
|
+
raw_events: list[dict] = []
|
|
98
|
+
parts: list[dict] = []
|
|
99
|
+
interactions: list[dict] = []
|
|
100
|
+
text_deltas: list[str] = []
|
|
101
|
+
final_text: str | None = None
|
|
102
|
+
response_id: str | None = None
|
|
103
|
+
conversation_group_id: str | None = None
|
|
104
|
+
updated_title: str | None = None
|
|
105
|
+
completed = False
|
|
106
|
+
|
|
107
|
+
for event in events:
|
|
108
|
+
data = event.get("data")
|
|
109
|
+
if data == "[DONE]":
|
|
110
|
+
continue
|
|
111
|
+
raw_events.append(event)
|
|
112
|
+
if not isinstance(data, dict):
|
|
113
|
+
continue
|
|
114
|
+
|
|
115
|
+
event_type = str(data.get("type") or event.get("event") or "message")
|
|
116
|
+
response_id = str(data.get("response_id") or response_id or "") or None
|
|
117
|
+
conversation_group_id = str(
|
|
118
|
+
data.get("conversation_group_id") or conversation_group_id or ""
|
|
119
|
+
) or None
|
|
120
|
+
|
|
121
|
+
if event_type == "response.part.delta":
|
|
122
|
+
delta = data.get("delta")
|
|
123
|
+
if data.get("part_kind") == "text" and isinstance(delta, str):
|
|
124
|
+
text_deltas.append(delta)
|
|
125
|
+
elif event_type in ("response.part.created", "response.part.completed"):
|
|
126
|
+
part = data.get("part")
|
|
127
|
+
if isinstance(part, dict):
|
|
128
|
+
parts.append({"event": event_type, "part": part})
|
|
129
|
+
content = part.get("content")
|
|
130
|
+
if (
|
|
131
|
+
event_type == "response.part.completed"
|
|
132
|
+
and part.get("part_kind") == "text"
|
|
133
|
+
and isinstance(content, dict)
|
|
134
|
+
and isinstance(content.get("content"), str)
|
|
135
|
+
):
|
|
136
|
+
final_text = content["content"]
|
|
137
|
+
elif event_type in ("response.interaction.created", "response.interaction.resolved"):
|
|
138
|
+
interactions.append(data)
|
|
139
|
+
elif event_type == "response.chat.title.updated":
|
|
140
|
+
name = data.get("name")
|
|
141
|
+
if isinstance(name, str):
|
|
142
|
+
updated_title = name
|
|
143
|
+
elif event_type == "response.failed":
|
|
144
|
+
message = data.get("message") or "Chat V2 stream failed"
|
|
145
|
+
raise TransportError(
|
|
146
|
+
str(message),
|
|
147
|
+
{
|
|
148
|
+
"code": data.get("code"),
|
|
149
|
+
"response_id": response_id,
|
|
150
|
+
"conversation_group_id": conversation_group_id,
|
|
151
|
+
"outcome_uncertain": True,
|
|
152
|
+
"events": raw_events,
|
|
153
|
+
},
|
|
154
|
+
)
|
|
155
|
+
elif event_type == "response.completed":
|
|
156
|
+
completed = True
|
|
157
|
+
|
|
158
|
+
if not completed:
|
|
159
|
+
raise TransportError(
|
|
160
|
+
"Chat V2 stream ended before response.completed. Inspect the history before retrying.",
|
|
161
|
+
{
|
|
162
|
+
"response_id": response_id,
|
|
163
|
+
"conversation_group_id": conversation_group_id,
|
|
164
|
+
"outcome_uncertain": True,
|
|
165
|
+
"events": raw_events,
|
|
166
|
+
},
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
return {
|
|
170
|
+
"stream": True,
|
|
171
|
+
"completed": True,
|
|
172
|
+
"response_id": response_id,
|
|
173
|
+
"conversation_group_id": conversation_group_id,
|
|
174
|
+
"final_text": final_text if final_text is not None else "".join(text_deltas),
|
|
175
|
+
"updated_title": updated_title,
|
|
176
|
+
"parts": parts,
|
|
177
|
+
"interactions": interactions,
|
|
178
|
+
"events": raw_events,
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def list_messages(
|
|
183
|
+
client: CodeerClient,
|
|
184
|
+
chat_id: int,
|
|
185
|
+
*,
|
|
186
|
+
external_user_id: Optional[str] = None,
|
|
187
|
+
limit: int = 500,
|
|
188
|
+
) -> dict:
|
|
189
|
+
params: dict[str, Any] = {"limit": limit, "offset": 0}
|
|
190
|
+
if external_user_id is not None:
|
|
191
|
+
params["external_user_id"] = external_user_id
|
|
192
|
+
return client.get(
|
|
193
|
+
f"/chats/{chat_id}/messages",
|
|
194
|
+
api_version="v2",
|
|
195
|
+
params=params,
|
|
196
|
+
)
|
|
84
197
|
|
|
85
198
|
|
|
86
199
|
def list_chats(client: CodeerClient) -> list[dict]:
|
|
200
|
+
"""List chats through the legacy v1 endpoint; Chat V2 has no list route."""
|
|
87
201
|
return client.get("/chats")
|
codeer_cli/cli.py
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
codeer check
|
|
4
4
|
codeer agent list|get|apply|diff|versions
|
|
5
|
+
codeer model list
|
|
5
6
|
codeer kb list|files|upload|node-rename|node-delete|faq-list|faq-get|faq-create|faq-update|faq-delete
|
|
6
7
|
codeer eval list|label-list|label-create|label-update|label-delete|case-update|case-delete|evaluators|evaluator-create|evaluator-update|run|export|reconcile|cases-apply|rubrics|rubrics-apply
|
|
7
8
|
codeer history list|get|conversations|negative-feedback|create|send
|
|
@@ -25,6 +26,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
25
26
|
epilog="""\
|
|
26
27
|
Safe workflow for coding agents:
|
|
27
28
|
codeer check --json
|
|
29
|
+
codeer model list --type text
|
|
28
30
|
codeer agent list
|
|
29
31
|
codeer agent get <agent-id> --full
|
|
30
32
|
codeer kb list
|
|
@@ -55,6 +57,12 @@ Use --out <path> for large raw artifacts; stdout defaults to compact summaries.
|
|
|
55
57
|
|
|
56
58
|
check.register(sub)
|
|
57
59
|
|
|
60
|
+
try:
|
|
61
|
+
from .commands import model as model_cmd
|
|
62
|
+
model_cmd.register(sub)
|
|
63
|
+
except ImportError:
|
|
64
|
+
pass
|
|
65
|
+
|
|
58
66
|
# Phase 2-4: agent, kb, eval commands will register here
|
|
59
67
|
try:
|
|
60
68
|
from .commands import agent as agent_cmd
|
codeer_cli/client.py
CHANGED
|
@@ -12,11 +12,12 @@ from __future__ import annotations
|
|
|
12
12
|
import json as json_lib
|
|
13
13
|
import os
|
|
14
14
|
from dataclasses import dataclass
|
|
15
|
-
from typing import Any, Iterable, Iterator, Mapping, Optional
|
|
15
|
+
from typing import Any, Iterable, Iterator, Literal, Mapping, Optional
|
|
16
16
|
|
|
17
17
|
import httpx
|
|
18
18
|
|
|
19
19
|
DEFAULT_CODEER_API_BASE = "https://api.codeer.ai"
|
|
20
|
+
ApiVersion = Literal["v1", "v2"]
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
class CodeerError(RuntimeError):
|
|
@@ -154,13 +155,14 @@ class CodeerClient:
|
|
|
154
155
|
method: str,
|
|
155
156
|
path: str,
|
|
156
157
|
*,
|
|
158
|
+
api_version: ApiVersion = "v1",
|
|
157
159
|
params: Optional[Mapping[str, Any]] = None,
|
|
158
160
|
json: Any = None,
|
|
159
161
|
files: Any = None,
|
|
160
162
|
data: Any = None,
|
|
161
163
|
timeout: Optional[float] = None,
|
|
162
164
|
) -> Any:
|
|
163
|
-
url =
|
|
165
|
+
url = _api_url(path, api_version=api_version)
|
|
164
166
|
request_kwargs: dict[str, Any] = {}
|
|
165
167
|
if timeout is not None:
|
|
166
168
|
request_kwargs["timeout"] = timeout
|
|
@@ -206,17 +208,28 @@ class CodeerClient:
|
|
|
206
208
|
method: str,
|
|
207
209
|
path: str,
|
|
208
210
|
*,
|
|
211
|
+
api_version: ApiVersion = "v1",
|
|
209
212
|
params: Optional[Mapping[str, Any]] = None,
|
|
210
213
|
json: Any = None,
|
|
214
|
+
timeout: Optional[float] = None,
|
|
211
215
|
) -> Iterator[dict]:
|
|
212
216
|
"""Yield parsed SSE events from a streaming endpoint (e.g. POST /chats/{id}/messages).
|
|
213
217
|
|
|
214
218
|
Each event is a dict like ``{"event": "message", "data": <parsed-json-or-str>}``.
|
|
215
219
|
"""
|
|
216
|
-
url =
|
|
220
|
+
url = _api_url(path, api_version=api_version)
|
|
217
221
|
method_upper = method.upper()
|
|
222
|
+
request_kwargs: dict[str, Any] = {}
|
|
223
|
+
if timeout is not None:
|
|
224
|
+
request_kwargs["timeout"] = timeout
|
|
218
225
|
try:
|
|
219
|
-
with self._client.stream(
|
|
226
|
+
with self._client.stream(
|
|
227
|
+
method_upper,
|
|
228
|
+
url,
|
|
229
|
+
params=params,
|
|
230
|
+
json=json,
|
|
231
|
+
**request_kwargs,
|
|
232
|
+
) as r:
|
|
220
233
|
if r.status_code >= 400:
|
|
221
234
|
body = r.read().decode("utf-8", "replace")
|
|
222
235
|
self._raise_for_error(r.status_code, body)
|
|
@@ -244,7 +257,7 @@ class CodeerClient:
|
|
|
244
257
|
method_upper,
|
|
245
258
|
path,
|
|
246
259
|
exc,
|
|
247
|
-
timeout_seconds=self.timeout,
|
|
260
|
+
timeout_seconds=timeout if timeout is not None else self.timeout,
|
|
248
261
|
) from exc
|
|
249
262
|
except httpx.RequestError as exc:
|
|
250
263
|
raise self._transport_error(method_upper, path, exc) from exc
|
|
@@ -314,6 +327,18 @@ def _maybe_json(raw: str) -> Any:
|
|
|
314
327
|
return raw
|
|
315
328
|
|
|
316
329
|
|
|
330
|
+
def _api_url(path: str, *, api_version: ApiVersion = "v1") -> str:
|
|
331
|
+
"""Resolve a relative domain path without forcing every API onto one version."""
|
|
332
|
+
if path.startswith("http"):
|
|
333
|
+
return path
|
|
334
|
+
normalized = path if path.startswith("/") else f"/{path}"
|
|
335
|
+
if normalized.startswith("/api/"):
|
|
336
|
+
return normalized
|
|
337
|
+
if api_version not in ("v1", "v2"):
|
|
338
|
+
raise ValueError(f"Unsupported API version: {api_version}")
|
|
339
|
+
return f"/api/{api_version}{normalized}"
|
|
340
|
+
|
|
341
|
+
|
|
317
342
|
def _workspace_names(profile: Mapping[str, Any]) -> dict[str, str]:
|
|
318
343
|
names: dict[str, str] = {}
|
|
319
344
|
for ws in profile.get("workspaces") or []:
|
codeer_cli/commands/agent.py
CHANGED
|
@@ -6,6 +6,7 @@ from pathlib import Path
|
|
|
6
6
|
from typing import Optional
|
|
7
7
|
|
|
8
8
|
from .. import agents as agents_mod
|
|
9
|
+
from .._validate import validate_human_handoff, validate_unified_tools
|
|
9
10
|
from ..client import CodeerClient
|
|
10
11
|
from ._util import log, print_json, strip_noisy_fields, truncate, write_json
|
|
11
12
|
|
|
@@ -102,6 +103,12 @@ def _tool_summary(tools: list[dict] | None) -> list[dict]:
|
|
|
102
103
|
|
|
103
104
|
def _agent_summary(agent: dict, *, full: bool = False) -> dict:
|
|
104
105
|
tools = agent.get("unified_tools") or agent.get("tools") or []
|
|
106
|
+
human_handoff = agent.get("human_handoff")
|
|
107
|
+
if not isinstance(human_handoff, dict):
|
|
108
|
+
meta = agent.get("meta")
|
|
109
|
+
human_handoff = meta.get("human_handoff") if isinstance(meta, dict) else {}
|
|
110
|
+
if not isinstance(human_handoff, dict):
|
|
111
|
+
human_handoff = {}
|
|
105
112
|
row = {
|
|
106
113
|
"id": agent.get("id"),
|
|
107
114
|
"name": agent.get("name"),
|
|
@@ -118,6 +125,7 @@ def _agent_summary(agent: dict, *, full: bool = False) -> dict:
|
|
|
118
125
|
"agent_type": agent.get("agent_type"),
|
|
119
126
|
"updated_at": agent.get("updated_at"),
|
|
120
127
|
"tool_count": len(tools),
|
|
128
|
+
"human_handoff_enabled": bool(human_handoff.get("enabled")),
|
|
121
129
|
"system_prompt_chars": len(agent.get("system_prompt") or ""),
|
|
122
130
|
}
|
|
123
131
|
if full:
|
|
@@ -125,6 +133,7 @@ def _agent_summary(agent: dict, *, full: bool = False) -> dict:
|
|
|
125
133
|
row["use_search"] = agent.get("use_search")
|
|
126
134
|
row["suggested_questions"] = agent.get("suggested_questions") or []
|
|
127
135
|
row["tools"] = _tool_summary(tools)
|
|
136
|
+
row["human_handoff"] = human_handoff
|
|
128
137
|
row["system_prompt_preview"] = truncate(agent.get("system_prompt") or "", 1200)
|
|
129
138
|
return row
|
|
130
139
|
|
|
@@ -153,6 +162,13 @@ def run_apply(args, client) -> int:
|
|
|
153
162
|
log(f"error: payload missing required field(s): {', '.join(missing)}")
|
|
154
163
|
return 2
|
|
155
164
|
|
|
165
|
+
try:
|
|
166
|
+
validated_tools = validate_unified_tools(body.get("unified_tools") or [])
|
|
167
|
+
validated_handoff = validate_human_handoff(body.get("human_handoff"))
|
|
168
|
+
except ValueError as exc:
|
|
169
|
+
log(f"error: invalid agent payload: {exc}")
|
|
170
|
+
return 2
|
|
171
|
+
|
|
156
172
|
if args.dry_run:
|
|
157
173
|
operation = "update" if args.agent_id else "create"
|
|
158
174
|
result = {
|
|
@@ -162,9 +178,14 @@ def run_apply(args, client) -> int:
|
|
|
162
178
|
"payload": str(Path(args.payload)),
|
|
163
179
|
"name": body.get("name"),
|
|
164
180
|
"system_prompt_chars": len(body.get("system_prompt") or ""),
|
|
165
|
-
"tool_count": len(
|
|
181
|
+
"tool_count": len(validated_tools),
|
|
166
182
|
"use_search": body.get("use_search", False),
|
|
167
183
|
"llm_model": body.get("llm_model"),
|
|
184
|
+
"human_handoff": {
|
|
185
|
+
"enabled": bool((validated_handoff or {}).get("enabled")),
|
|
186
|
+
"idle_timeout_minutes": (validated_handoff or {}).get("idle_timeout_minutes"),
|
|
187
|
+
"instructions_chars": len((validated_handoff or {}).get("handoff_instructions") or ""),
|
|
188
|
+
},
|
|
168
189
|
"version_note": args.note if args.agent_id else None,
|
|
169
190
|
"would_write_server_state": True,
|
|
170
191
|
"next_step": "Review this summary, then rerun without --dry-run after approval.",
|
|
@@ -178,7 +199,7 @@ def run_apply(args, client) -> int:
|
|
|
178
199
|
client, args.agent_id,
|
|
179
200
|
name=body["name"],
|
|
180
201
|
system_prompt=body["system_prompt"],
|
|
181
|
-
unified_tools=
|
|
202
|
+
unified_tools=validated_tools,
|
|
182
203
|
use_search=body.get("use_search", False),
|
|
183
204
|
version_note=args.note,
|
|
184
205
|
description=body.get("description"),
|
|
@@ -186,6 +207,7 @@ def run_apply(args, client) -> int:
|
|
|
186
207
|
suggested_questions=body.get("suggested_questions") or [],
|
|
187
208
|
primary_object_ids=body.get("primary_object_ids") or [],
|
|
188
209
|
attachment_ids=body.get("attachment_ids") or [],
|
|
210
|
+
human_handoff=validated_handoff,
|
|
189
211
|
)
|
|
190
212
|
agent_id = args.agent_id
|
|
191
213
|
log(f"PUT /agents/{agent_id} ok")
|
|
@@ -197,13 +219,14 @@ def run_apply(args, client) -> int:
|
|
|
197
219
|
workspace_id=body["workspace_id"],
|
|
198
220
|
name=body["name"],
|
|
199
221
|
system_prompt=body["system_prompt"],
|
|
200
|
-
unified_tools=
|
|
222
|
+
unified_tools=validated_tools,
|
|
201
223
|
use_search=body.get("use_search", False),
|
|
202
224
|
description=body.get("description"),
|
|
203
225
|
llm_model=body.get("llm_model"),
|
|
204
226
|
suggested_questions=body.get("suggested_questions") or [],
|
|
205
227
|
primary_object_ids=body.get("primary_object_ids") or [],
|
|
206
228
|
attachment_ids=body.get("attachment_ids") or [],
|
|
229
|
+
human_handoff=validated_handoff,
|
|
207
230
|
)
|
|
208
231
|
agent_id = agent["id"]
|
|
209
232
|
log(f"POST /agents ok, id={agent_id}")
|
codeer_cli/commands/history.py
CHANGED
|
@@ -76,8 +76,8 @@ def register(subparsers):
|
|
|
76
76
|
p.add_argument("--user", default=None, help="external_user_id to associate with the history")
|
|
77
77
|
p.add_argument("--message", action="append", required=True,
|
|
78
78
|
help="User message to send. Repeat for multi-turn histories.")
|
|
79
|
-
p.add_argument("--timeout", type=float, default=
|
|
80
|
-
help="Per-message
|
|
79
|
+
p.add_argument("--timeout", type=float, default=240.0,
|
|
80
|
+
help="Per-message SSE read timeout in seconds (default: 240).")
|
|
81
81
|
p.add_argument("--out", default=None,
|
|
82
82
|
help="Write complete create response/conversation artifact to this file; stdout stays compact.")
|
|
83
83
|
p.set_defaults(func=run_create)
|
|
@@ -91,8 +91,8 @@ def register(subparsers):
|
|
|
91
91
|
help="external_user_id override (defaults to the history's user)")
|
|
92
92
|
p.add_argument("--message", action="append", required=True,
|
|
93
93
|
help="User message to send. Repeat to append multiple turns.")
|
|
94
|
-
p.add_argument("--timeout", type=float, default=
|
|
95
|
-
help="Per-message
|
|
94
|
+
p.add_argument("--timeout", type=float, default=240.0,
|
|
95
|
+
help="Per-message SSE read timeout in seconds (default: 240).")
|
|
96
96
|
p.add_argument("--out", default=None,
|
|
97
97
|
help="Write complete send response/conversation artifact to this file; stdout stays compact.")
|
|
98
98
|
p.set_defaults(func=run_send)
|
|
@@ -285,15 +285,19 @@ def _send_messages(
|
|
|
285
285
|
for idx, message in enumerate(messages, 1):
|
|
286
286
|
log(f"sending turn {idx}/{len(messages)}")
|
|
287
287
|
try:
|
|
288
|
-
|
|
288
|
+
stream = chats_mod.send_published_agent_message(
|
|
289
289
|
client,
|
|
290
290
|
chat_id=history_id,
|
|
291
291
|
message=message,
|
|
292
292
|
agent_id=agent_id,
|
|
293
293
|
external_user_id=external_user_id,
|
|
294
|
-
stream=
|
|
294
|
+
stream=True,
|
|
295
295
|
timeout=timeout,
|
|
296
296
|
)
|
|
297
|
+
if isinstance(stream, dict):
|
|
298
|
+
result = stream
|
|
299
|
+
else:
|
|
300
|
+
result = chats_mod.collect_stream(stream)
|
|
297
301
|
except TransportError as exc:
|
|
298
302
|
body = dict(exc.body) if isinstance(exc.body, dict) else {}
|
|
299
303
|
body.update({"history_id": history_id, "turn": idx, "turn_count": len(messages)})
|
|
@@ -337,14 +341,19 @@ def run_create(args, client) -> int:
|
|
|
337
341
|
timeout=args.timeout,
|
|
338
342
|
)
|
|
339
343
|
|
|
340
|
-
|
|
344
|
+
chat_messages = chats_mod.list_messages(
|
|
345
|
+
client,
|
|
346
|
+
history_id,
|
|
347
|
+
external_user_id=args.user,
|
|
348
|
+
)
|
|
349
|
+
conversation_parts = chat_messages.get("messages") or []
|
|
341
350
|
out = {
|
|
342
351
|
"agent_id": agent_id,
|
|
343
352
|
"history_id": history_id,
|
|
344
353
|
"external_user_id": args.user,
|
|
345
354
|
"url": _history_url(client, workspace_id, history_id),
|
|
346
355
|
"messages": message_results,
|
|
347
|
-
"
|
|
356
|
+
"conversation_parts": conversation_parts,
|
|
348
357
|
}
|
|
349
358
|
write_json(args.out, strip_noisy_fields(out))
|
|
350
359
|
print_json({
|
|
@@ -353,7 +362,7 @@ def run_create(args, client) -> int:
|
|
|
353
362
|
"external_user_id": args.user,
|
|
354
363
|
"url": out["url"],
|
|
355
364
|
"message_count": len(message_results),
|
|
356
|
-
"
|
|
365
|
+
"part_count": len(conversation_parts),
|
|
357
366
|
"wrote_full_detail": bool(args.out),
|
|
358
367
|
})
|
|
359
368
|
return 0
|
|
@@ -395,14 +404,19 @@ def run_send(args, client) -> int:
|
|
|
395
404
|
messages=args.message,
|
|
396
405
|
timeout=args.timeout,
|
|
397
406
|
)
|
|
398
|
-
|
|
407
|
+
chat_messages = chats_mod.list_messages(
|
|
408
|
+
client,
|
|
409
|
+
args.history_id,
|
|
410
|
+
external_user_id=external_user_id,
|
|
411
|
+
)
|
|
412
|
+
conversation_parts = chat_messages.get("messages") or []
|
|
399
413
|
out = {
|
|
400
414
|
"agent_id": agent_id,
|
|
401
415
|
"history_id": args.history_id,
|
|
402
416
|
"external_user_id": external_user_id,
|
|
403
417
|
"url": _history_url(client, workspace_id, args.history_id),
|
|
404
418
|
"messages": message_results,
|
|
405
|
-
"
|
|
419
|
+
"conversation_parts": conversation_parts,
|
|
406
420
|
}
|
|
407
421
|
write_json(args.out, strip_noisy_fields(out))
|
|
408
422
|
print_json({
|
|
@@ -411,7 +425,7 @@ def run_send(args, client) -> int:
|
|
|
411
425
|
"external_user_id": external_user_id,
|
|
412
426
|
"url": out["url"],
|
|
413
427
|
"message_count": len(message_results),
|
|
414
|
-
"
|
|
428
|
+
"part_count": len(conversation_parts),
|
|
415
429
|
"wrote_full_detail": bool(args.out),
|
|
416
430
|
})
|
|
417
431
|
return 0
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from .. import models as models_mod
|
|
4
|
+
from ._util import print_json, strip_noisy_fields, write_json
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def register(subparsers):
|
|
8
|
+
model = subparsers.add_parser("model", help="Available LLM models")
|
|
9
|
+
sub = model.add_subparsers(dest="action", required=True)
|
|
10
|
+
|
|
11
|
+
p = sub.add_parser("list", help="List active cloud LLM models available to agents")
|
|
12
|
+
p.add_argument("--type", choices=("text", "image"), default=None,
|
|
13
|
+
help="Filter by model type")
|
|
14
|
+
p.add_argument("--full", action="store_true",
|
|
15
|
+
help="Include modalities, pricing, and creation metadata")
|
|
16
|
+
p.add_argument("--out", default=None,
|
|
17
|
+
help="Write the complete model response to this file")
|
|
18
|
+
p.set_defaults(func=run_list)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _model_summary(model: dict, *, full: bool = False) -> dict:
|
|
22
|
+
row = {
|
|
23
|
+
"display_name": model.get("display_name"),
|
|
24
|
+
"model_id": model.get("model_id"),
|
|
25
|
+
"provider": model.get("provider"),
|
|
26
|
+
"model_type": model.get("model_type"),
|
|
27
|
+
}
|
|
28
|
+
if full:
|
|
29
|
+
row.update({
|
|
30
|
+
"input_modalities": model.get("input_modalities") or [],
|
|
31
|
+
"input_credits_per_million_tokens": model.get("input_credits_per_million_tokens"),
|
|
32
|
+
"output_credits_per_million_tokens": model.get("output_credits_per_million_tokens"),
|
|
33
|
+
"created_at": model.get("created_at"),
|
|
34
|
+
})
|
|
35
|
+
return row
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def run_list(args, client) -> int:
|
|
39
|
+
result = models_mod.list_available(client, model_type=args.type)
|
|
40
|
+
full_result = strip_noisy_fields(result)
|
|
41
|
+
write_json(args.out, full_result)
|
|
42
|
+
print_json([_model_summary(model, full=args.full) for model in result])
|
|
43
|
+
return 0
|
codeer_cli/models.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""Available LLM model discovery."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
from .client import CodeerClient
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def list_available(
|
|
11
|
+
client: CodeerClient,
|
|
12
|
+
*,
|
|
13
|
+
model_type: Optional[str] = None,
|
|
14
|
+
) -> list[dict]:
|
|
15
|
+
params = {"model_type": model_type} if model_type else None
|
|
16
|
+
return client.get("/llm/models", params=params)
|
codeer_cli/parse.py
CHANGED
|
@@ -9,21 +9,20 @@ Parsers are deliberately tolerant: missing/extra fields don't raise, casing
|
|
|
9
9
|
mismatches (``FILE`` vs ``file``) are normalized to lowercase. They are NOT a
|
|
10
10
|
schema validator.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
LEGACY V1 HISTORY GAP, called out here so you don't waste time:
|
|
13
13
|
|
|
14
|
-
Tool **arguments**
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
dropped after the assistant turn is saved. What you CAN recover from a
|
|
18
|
-
history is:
|
|
14
|
+
Tool **arguments** and **outputs** are not available on the legacy
|
|
15
|
+
``Conversation`` row returned by ``/histories/{id}/conversations``. What you
|
|
16
|
+
CAN recover from that legacy history shape is:
|
|
19
17
|
|
|
20
18
|
- tool name + call id (regex over ``content``: ``<tool id=...>name</tool>``)
|
|
21
19
|
- per-call token usage (``meta.token_usage.tool_calls[]``)
|
|
22
20
|
- retrieved primary sources (``primary_sources[]`` — the end-to-end trace)
|
|
23
21
|
- assistant's final text (``content`` with tool markers stripped)
|
|
24
22
|
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
Chat V2 structured SSE and ``GET /api/v2/chats/{id}/messages`` expose
|
|
24
|
+
persisted tool-call and tool-return parts. Use those surfaces when raw tool
|
|
25
|
+
I/O or exact event order matters.
|
|
27
26
|
"""
|
|
28
27
|
|
|
29
28
|
from __future__ import annotations
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codeer-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.11
|
|
4
4
|
Summary: Command line tools for managing Codeer agents over the Codeer API.
|
|
5
5
|
Project-URL: Homepage, https://www.codeer.ai
|
|
6
6
|
Author: Codeer.AI
|
|
@@ -117,6 +117,35 @@ Validate setup before API work:
|
|
|
117
117
|
codeer check
|
|
118
118
|
```
|
|
119
119
|
|
|
120
|
+
List the active cloud models without opening the Codeer web app:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
codeer model list --type text
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Agent human handoff
|
|
127
|
+
|
|
128
|
+
`codeer agent apply` accepts the same `human_handoff` object as the Agent API.
|
|
129
|
+
The dry-run validates it and shows whether handoff is enabled before any server
|
|
130
|
+
write:
|
|
131
|
+
|
|
132
|
+
```json
|
|
133
|
+
{
|
|
134
|
+
"name": "Support Agent",
|
|
135
|
+
"system_prompt": "Help the user safely.",
|
|
136
|
+
"human_handoff": {
|
|
137
|
+
"enabled": true,
|
|
138
|
+
"idle_timeout_minutes": null,
|
|
139
|
+
"handoff_instructions": "Hand off when the user asks for a person."
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
`idle_timeout_minutes` must be a positive integer or `null`. Human handoff only
|
|
145
|
+
becomes available in live published-agent conversations with a non-empty
|
|
146
|
+
`external_user_id`; editor Live Test conversations are internal and cannot
|
|
147
|
+
activate human mode.
|
|
148
|
+
|
|
120
149
|
## Upgrade and uninstall
|
|
121
150
|
|
|
122
151
|
Upgrade the CLI:
|
|
@@ -143,15 +172,17 @@ Use this pattern during agent lifecycle work:
|
|
|
143
172
|
```bash
|
|
144
173
|
codeer agent list
|
|
145
174
|
codeer history list --agent <agent-id> --limit 50
|
|
146
|
-
codeer history create --agent <agent-id> --message "Review this plan" --timeout
|
|
147
|
-
codeer history send <history-id> --message "Use the recommended options" --timeout
|
|
175
|
+
codeer history create --agent <agent-id> --message "Review this plan" --timeout 240
|
|
176
|
+
codeer history send <history-id> --message "Use the recommended options" --timeout 240
|
|
148
177
|
codeer eval run --agent <agent-id> --cases <case-ids> --evaluator <evaluator-id> --out .codeer/eval_run.json
|
|
149
178
|
```
|
|
150
179
|
|
|
151
180
|
`history create` and `history send` use the agent's current published version.
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
181
|
+
They use Chat V2 structured SSE with `stream: true`; their per-message read
|
|
182
|
+
timeout defaults to 240 seconds. Success requires a `response.completed`
|
|
183
|
+
event. If the stream times out, reports `response.failed`, or disconnects
|
|
184
|
+
early, inspect the history before retrying: the server may already have
|
|
185
|
+
persisted the turn.
|
|
155
186
|
|
|
156
187
|
Eval case label commands always operate on the active API-key workspace. They
|
|
157
188
|
do not accept a workspace override; switch CLI profiles to target another
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
codeer_cli/__init__.py,sha256=z3rkUdVCt11c8QE3-YFkG5cFObM6QhPe2yKugA8k3aA,1915
|
|
2
|
+
codeer_cli/_validate.py,sha256=lqMwkhsBy1g0o5CINBp1HdBCjMawgUaEsZygjJaAPL0,6943
|
|
3
|
+
codeer_cli/agents.py,sha256=HWfz6QgU4wn07df5s3twxVJfAhb0vLJHIfxTjvDMUzU,6045
|
|
4
|
+
codeer_cli/chats.py,sha256=HaQYqbwv5Kq0RgBTKaa9vNYMlsBQonh85-v8yw_rJW8,6609
|
|
5
|
+
codeer_cli/cli.py,sha256=ghZMn7LUxDDvb3f82mDYW5hIFLk7dtlrCrGGP40Gygw,4637
|
|
6
|
+
codeer_cli/client.py,sha256=apUycjK-OvHEquAj58FqOy8FHmHjA6K9dScaYQ4M-VU,13175
|
|
7
|
+
codeer_cli/constants.py,sha256=D1pV3wCoqYybrKGKeoupYjjFWLfaFviKp1yL7oh6Qso,2323
|
|
8
|
+
codeer_cli/eval_.py,sha256=2Tz4qokctocZ0fqLYL8Sk9UV8yJ7p-9zYiUFljsRRCs,17478
|
|
9
|
+
codeer_cli/histories.py,sha256=tk28git_peX4x703CIDU8u72JtlGaytyrtlHfxlK-7A,5979
|
|
10
|
+
codeer_cli/kb.py,sha256=Ad4h65NByq5Rq5BTeMghLTKlWRhmOC2jxL0BaTGX3EM,10631
|
|
11
|
+
codeer_cli/models.py,sha256=Ama9fx8hyRMdysiUNA3Ty0sYIYwzFDkOcAZg-id3-vQ,362
|
|
12
|
+
codeer_cli/parse.py,sha256=-txMtIxPfeg_i4McEskVKxo8p4438gKS-sFTI38RoBs,21772
|
|
13
|
+
codeer_cli/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
+
codeer_cli/commands/_util.py,sha256=X9-9cYgnBYz94hVG4Pe9SsI97DHNv3HE5_7GlPMn93I,1708
|
|
15
|
+
codeer_cli/commands/agent.py,sha256=6qtYcQYgdJE26V6Nw6parB_3RYAYXcOpPE9iJLuLGHo,15888
|
|
16
|
+
codeer_cli/commands/check.py,sha256=lTxolx1mIJ8jldPhJ5FXqie9nbCLVOO-sDPOHTSy1-w,3817
|
|
17
|
+
codeer_cli/commands/eval_cmd.py,sha256=Zlt_YIA3D5-Y8WL1HZ6PxZgL-TcwFTDoXvO2gv7yfOM,71989
|
|
18
|
+
codeer_cli/commands/history.py,sha256=qb0YXggbJuSahDp-l2A6Abj3g-LpIXWzgHadTA-4ZIU,16500
|
|
19
|
+
codeer_cli/commands/kb.py,sha256=kVEinBVM6NN8_0djOqIQErh46dLmArwFngXMNzvGeAI,28345
|
|
20
|
+
codeer_cli/commands/model.py,sha256=91LDo_YeHi3POZV7KTCkoCcgtK5p87LwrHX1lZgfCps,1698
|
|
21
|
+
codeer_cli/commands/profile.py,sha256=IdlXC_6cqobsfN3JRrAnt-1OgBUsIFneS9QtR4Un6Kc,6521
|
|
22
|
+
codeer_cli-0.1.11.dist-info/METADATA,sha256=tPOpQ2LTjMvWLk_KjwzXOKtJWr8Iq3XV0j0HEiScI88,7520
|
|
23
|
+
codeer_cli-0.1.11.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
|
|
24
|
+
codeer_cli-0.1.11.dist-info/entry_points.txt,sha256=-nXIrlm5SR5r7gg3y8AS0tN66MwmvNHsrlwLNQNGD50,47
|
|
25
|
+
codeer_cli-0.1.11.dist-info/RECORD,,
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
codeer_cli/__init__.py,sha256=slzDQ-Le6vl5PrimV6ZKmcDcNsQ7my7yG-Is1JVlbi4,1851
|
|
2
|
-
codeer_cli/_validate.py,sha256=pKUJa2TyTpERx5xmiYNZRn7tFqDxLZ2fF1rHAf1oz14,5415
|
|
3
|
-
codeer_cli/agents.py,sha256=diodgiGhXlowEi8sbCzcSK1qSeCLF2fBe6QBs3Sq_x8,5617
|
|
4
|
-
codeer_cli/chats.py,sha256=pww_AVByTh_AT2Td0gj2OomJrcw7D-cjDIpQ0ZQOlrM,2728
|
|
5
|
-
codeer_cli/cli.py,sha256=tdHbAEqtfCWFVT1iecfWKh61P1IvjG59F9hfC4Srr0c,4455
|
|
6
|
-
codeer_cli/client.py,sha256=x-5sx06dgS6P8siKOTCVNM8JlBST5e6PsOD83KKkpN8,12361
|
|
7
|
-
codeer_cli/constants.py,sha256=D1pV3wCoqYybrKGKeoupYjjFWLfaFviKp1yL7oh6Qso,2323
|
|
8
|
-
codeer_cli/eval_.py,sha256=2Tz4qokctocZ0fqLYL8Sk9UV8yJ7p-9zYiUFljsRRCs,17478
|
|
9
|
-
codeer_cli/histories.py,sha256=tk28git_peX4x703CIDU8u72JtlGaytyrtlHfxlK-7A,5979
|
|
10
|
-
codeer_cli/kb.py,sha256=Ad4h65NByq5Rq5BTeMghLTKlWRhmOC2jxL0BaTGX3EM,10631
|
|
11
|
-
codeer_cli/parse.py,sha256=qrjZn0MUTjGfucp4cwxy8Pt7WS-0x15kK5F7kWTY8Ps,21818
|
|
12
|
-
codeer_cli/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
-
codeer_cli/commands/_util.py,sha256=X9-9cYgnBYz94hVG4Pe9SsI97DHNv3HE5_7GlPMn93I,1708
|
|
14
|
-
codeer_cli/commands/agent.py,sha256=amvfVVrbPOkbYKCGvA6EJB30C-aY6WSdfR7u7FklXbs,14793
|
|
15
|
-
codeer_cli/commands/check.py,sha256=lTxolx1mIJ8jldPhJ5FXqie9nbCLVOO-sDPOHTSy1-w,3817
|
|
16
|
-
codeer_cli/commands/eval_cmd.py,sha256=Zlt_YIA3D5-Y8WL1HZ6PxZgL-TcwFTDoXvO2gv7yfOM,71989
|
|
17
|
-
codeer_cli/commands/history.py,sha256=bVBlCMStTvTxG23bggASHkHKuI0RMPdiXjcrs3U04yw,16081
|
|
18
|
-
codeer_cli/commands/kb.py,sha256=kVEinBVM6NN8_0djOqIQErh46dLmArwFngXMNzvGeAI,28345
|
|
19
|
-
codeer_cli/commands/profile.py,sha256=IdlXC_6cqobsfN3JRrAnt-1OgBUsIFneS9QtR4Un6Kc,6521
|
|
20
|
-
codeer_cli-0.1.9.dist-info/METADATA,sha256=XlAf4gazUUSZ8uk9YRBxOYCiJ-C13Tru02G548cqI5M,6605
|
|
21
|
-
codeer_cli-0.1.9.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
|
|
22
|
-
codeer_cli-0.1.9.dist-info/entry_points.txt,sha256=-nXIrlm5SR5r7gg3y8AS0tN66MwmvNHsrlwLNQNGD50,47
|
|
23
|
-
codeer_cli-0.1.9.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|