codeer-cli 0.1.12__tar.gz → 0.1.13__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 (39) hide show
  1. {codeer_cli-0.1.12 → codeer_cli-0.1.13}/PKG-INFO +2 -2
  2. {codeer_cli-0.1.12 → codeer_cli-0.1.13}/pyproject.toml +1 -1
  3. {codeer_cli-0.1.12 → codeer_cli-0.1.13}/src/codeer_cli/agents.py +6 -0
  4. {codeer_cli-0.1.12 → codeer_cli-0.1.13}/src/codeer_cli/commands/agent.py +15 -1
  5. codeer_cli-0.1.13/tests/test_agent_model_settings.py +230 -0
  6. {codeer_cli-0.1.12 → codeer_cli-0.1.13}/uv.lock +1 -1
  7. {codeer_cli-0.1.12 → codeer_cli-0.1.13}/.gitignore +0 -0
  8. {codeer_cli-0.1.12 → codeer_cli-0.1.13}/API_REFERENCE.md +0 -0
  9. {codeer_cli-0.1.12 → codeer_cli-0.1.13}/README.md +0 -0
  10. {codeer_cli-0.1.12 → codeer_cli-0.1.13}/src/codeer_cli/__init__.py +0 -0
  11. {codeer_cli-0.1.12 → codeer_cli-0.1.13}/src/codeer_cli/_validate.py +0 -0
  12. {codeer_cli-0.1.12 → codeer_cli-0.1.13}/src/codeer_cli/chats.py +0 -0
  13. {codeer_cli-0.1.12 → codeer_cli-0.1.13}/src/codeer_cli/cli.py +0 -0
  14. {codeer_cli-0.1.12 → codeer_cli-0.1.13}/src/codeer_cli/client.py +0 -0
  15. {codeer_cli-0.1.12 → codeer_cli-0.1.13}/src/codeer_cli/commands/__init__.py +0 -0
  16. {codeer_cli-0.1.12 → codeer_cli-0.1.13}/src/codeer_cli/commands/_util.py +0 -0
  17. {codeer_cli-0.1.12 → codeer_cli-0.1.13}/src/codeer_cli/commands/check.py +0 -0
  18. {codeer_cli-0.1.12 → codeer_cli-0.1.13}/src/codeer_cli/commands/eval_cmd.py +0 -0
  19. {codeer_cli-0.1.12 → codeer_cli-0.1.13}/src/codeer_cli/commands/history.py +0 -0
  20. {codeer_cli-0.1.12 → codeer_cli-0.1.13}/src/codeer_cli/commands/kb.py +0 -0
  21. {codeer_cli-0.1.12 → codeer_cli-0.1.13}/src/codeer_cli/commands/model.py +0 -0
  22. {codeer_cli-0.1.12 → codeer_cli-0.1.13}/src/codeer_cli/commands/profile.py +0 -0
  23. {codeer_cli-0.1.12 → codeer_cli-0.1.13}/src/codeer_cli/constants.py +0 -0
  24. {codeer_cli-0.1.12 → codeer_cli-0.1.13}/src/codeer_cli/eval_.py +0 -0
  25. {codeer_cli-0.1.12 → codeer_cli-0.1.13}/src/codeer_cli/histories.py +0 -0
  26. {codeer_cli-0.1.12 → codeer_cli-0.1.13}/src/codeer_cli/kb.py +0 -0
  27. {codeer_cli-0.1.12 → codeer_cli-0.1.13}/src/codeer_cli/models.py +0 -0
  28. {codeer_cli-0.1.12 → codeer_cli-0.1.13}/src/codeer_cli/parse.py +0 -0
  29. {codeer_cli-0.1.12 → codeer_cli-0.1.13}/tests/test_agent_handoff.py +0 -0
  30. {codeer_cli-0.1.12 → codeer_cli-0.1.13}/tests/test_chats_v2.py +0 -0
  31. {codeer_cli-0.1.12 → codeer_cli-0.1.13}/tests/test_client_transport.py +0 -0
  32. {codeer_cli-0.1.12 → codeer_cli-0.1.13}/tests/test_eval_labels.py +0 -0
  33. {codeer_cli-0.1.12 → codeer_cli-0.1.13}/tests/test_eval_pairs.py +0 -0
  34. {codeer_cli-0.1.12 → codeer_cli-0.1.13}/tests/test_history_read.py +0 -0
  35. {codeer_cli-0.1.12 → codeer_cli-0.1.13}/tests/test_history_send.py +0 -0
  36. {codeer_cli-0.1.12 → codeer_cli-0.1.13}/tests/test_kb_nodes.py +0 -0
  37. {codeer_cli-0.1.12 → codeer_cli-0.1.13}/tests/test_kb_ranges.py +0 -0
  38. {codeer_cli-0.1.12 → codeer_cli-0.1.13}/tests/test_models.py +0 -0
  39. {codeer_cli-0.1.12 → codeer_cli-0.1.13}/tests/test_util.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: codeer-cli
3
- Version: 0.1.12
3
+ Version: 0.1.13
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
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "codeer-cli"
7
- version = "0.1.12"
7
+ version = "0.1.13"
8
8
  description = "Command line tools for managing Codeer agents over the Codeer API."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -20,6 +20,7 @@ def create(
20
20
  unified_tools: Optional[List[dict]] = None,
21
21
  use_search: bool = False,
22
22
  llm_model: Optional[str] = None,
23
+ llm_model_settings: Optional[dict[str, dict[str, Any]]] = None,
23
24
  description: Optional[str] = None,
24
25
  suggested_questions: Optional[List[str]] = None,
25
26
  primary_object_ids: Optional[List[int]] = None,
@@ -41,6 +42,8 @@ def create(
41
42
  body["description"] = description
42
43
  if llm_model is not None:
43
44
  body["llm_model"] = llm_model
45
+ if llm_model_settings is not None:
46
+ body["llm_model_settings"] = llm_model_settings
44
47
  if validated_handoff is not None:
45
48
  body["human_handoff"] = validated_handoff
46
49
  return client.post("/external/agents", json=body)
@@ -57,6 +60,7 @@ def update(
57
60
  version_note: str = "",
58
61
  description: Optional[str] = None,
59
62
  llm_model: Optional[str] = None,
63
+ llm_model_settings: Optional[dict[str, dict[str, Any]]] = None,
60
64
  suggested_questions: Optional[List[str]] = None,
61
65
  primary_object_ids: Optional[List[int]] = None,
62
66
  attachment_ids: Optional[List[str]] = None,
@@ -79,6 +83,8 @@ def update(
79
83
  body["description"] = description
80
84
  if llm_model is not None:
81
85
  body["llm_model"] = llm_model
86
+ if llm_model_settings is not None:
87
+ body["llm_model_settings"] = llm_model_settings
82
88
  if validated_handoff is not None:
83
89
  body["human_handoff"] = validated_handoff
84
90
  return client.patch(f"/external/agents/{agent_id}", json=body)
@@ -89,7 +89,9 @@ def register(subparsers):
89
89
  def _tool_summary(tools: list[dict] | None) -> list[dict]:
90
90
  out = []
91
91
  for t in tools or []:
92
- form = t.get("custom_form_schema") if isinstance(t.get("custom_form_schema"), dict) else {}
92
+ form = t.get("custom_form_schema")
93
+ if not isinstance(form, dict):
94
+ form = {}
93
95
  out.append({
94
96
  "id": t.get("id"),
95
97
  "type": t.get("type"),
@@ -169,6 +171,8 @@ def run_apply(args, client) -> int:
169
171
  log(f"error: invalid agent payload: {exc}")
170
172
  return 2
171
173
 
174
+ llm_model_settings_provided = "llm_model_settings" in body
175
+
172
176
  if args.dry_run:
173
177
  operation = "update" if args.agent_id else "create"
174
178
  result = {
@@ -181,6 +185,8 @@ def run_apply(args, client) -> int:
181
185
  "tool_count": len(validated_tools),
182
186
  "use_search": body.get("use_search", False),
183
187
  "llm_model": body.get("llm_model"),
188
+ "llm_model_settings_provided": llm_model_settings_provided,
189
+ "llm_model_settings": body.get("llm_model_settings"),
184
190
  "human_handoff": {
185
191
  "enabled": bool((validated_handoff or {}).get("enabled")),
186
192
  "idle_timeout_minutes": (validated_handoff or {}).get("idle_timeout_minutes"),
@@ -193,6 +199,12 @@ def run_apply(args, client) -> int:
193
199
  print_json(result)
194
200
  return 0
195
201
 
202
+ model_settings_kwargs = (
203
+ {"llm_model_settings": body["llm_model_settings"]}
204
+ if llm_model_settings_provided
205
+ else {}
206
+ )
207
+
196
208
  if args.agent_id:
197
209
  body.pop("workspace_id", None)
198
210
  agents_mod.update(
@@ -208,6 +220,7 @@ def run_apply(args, client) -> int:
208
220
  primary_object_ids=body.get("primary_object_ids") or [],
209
221
  attachment_ids=body.get("attachment_ids") or [],
210
222
  human_handoff=validated_handoff,
223
+ **model_settings_kwargs,
211
224
  )
212
225
  agent_id = args.agent_id
213
226
  log(f"PUT /agents/{agent_id} ok")
@@ -227,6 +240,7 @@ def run_apply(args, client) -> int:
227
240
  primary_object_ids=body.get("primary_object_ids") or [],
228
241
  attachment_ids=body.get("attachment_ids") or [],
229
242
  human_handoff=validated_handoff,
243
+ **model_settings_kwargs,
230
244
  )
231
245
  agent_id = agent["id"]
232
246
  log(f"POST /agents ok, id={agent_id}")
@@ -0,0 +1,230 @@
1
+ from __future__ import annotations
2
+
3
+ import contextlib
4
+ import io
5
+ import json
6
+ import tempfile
7
+ import unittest
8
+ from pathlib import Path
9
+ from types import SimpleNamespace
10
+ from unittest.mock import patch
11
+
12
+ from codeer_cli import agents
13
+ from codeer_cli.commands import agent as agent_cmd
14
+
15
+
16
+ MODEL_ID = "openai/gpt-5.6-luna"
17
+ MODEL_SETTINGS = {MODEL_ID: {"thinking": "xhigh"}}
18
+
19
+
20
+ class FakeClient:
21
+ def __init__(self) -> None:
22
+ self.calls: list[tuple[str, str, dict]] = []
23
+
24
+ def post(self, path: str, **kwargs):
25
+ self.calls.append(("POST", path, kwargs))
26
+ return {"id": "agent-1"}
27
+
28
+ def patch(self, path: str, **kwargs):
29
+ self.calls.append(("PATCH", path, kwargs))
30
+ return {"id": "agent-1"}
31
+
32
+
33
+ class AgentModelSettingsPayloadTests(unittest.TestCase):
34
+ def test_create_forwards_model_settings(self) -> None:
35
+ client = FakeClient()
36
+
37
+ agents.create(
38
+ client, # type: ignore[arg-type]
39
+ workspace_id="workspace-1",
40
+ name="Agent",
41
+ system_prompt="Help safely.",
42
+ llm_model=MODEL_ID,
43
+ llm_model_settings=MODEL_SETTINGS,
44
+ )
45
+
46
+ self.assertEqual(client.calls[0][0:2], ("POST", "/external/agents"))
47
+ self.assertEqual(
48
+ client.calls[0][2]["json"]["llm_model_settings"],
49
+ MODEL_SETTINGS,
50
+ )
51
+
52
+ def test_update_forwards_model_settings(self) -> None:
53
+ client = FakeClient()
54
+
55
+ agents.update(
56
+ client, # type: ignore[arg-type]
57
+ "agent-1",
58
+ name="Agent",
59
+ system_prompt="Help safely.",
60
+ unified_tools=[],
61
+ use_search=False,
62
+ llm_model=MODEL_ID,
63
+ llm_model_settings=MODEL_SETTINGS,
64
+ )
65
+
66
+ self.assertEqual(
67
+ client.calls[0][0:2],
68
+ ("PATCH", "/external/agents/agent-1"),
69
+ )
70
+ self.assertEqual(
71
+ client.calls[0][2]["json"]["llm_model_settings"],
72
+ MODEL_SETTINGS,
73
+ )
74
+
75
+ def test_update_forwards_empty_map_to_clear_settings(self) -> None:
76
+ client = FakeClient()
77
+
78
+ agents.update(
79
+ client, # type: ignore[arg-type]
80
+ "agent-1",
81
+ name="Agent",
82
+ system_prompt="Help safely.",
83
+ unified_tools=[],
84
+ use_search=False,
85
+ llm_model_settings={},
86
+ )
87
+
88
+ self.assertEqual(client.calls[0][2]["json"]["llm_model_settings"], {})
89
+
90
+ def test_update_omits_model_settings_when_unspecified(self) -> None:
91
+ client = FakeClient()
92
+
93
+ agents.update(
94
+ client, # type: ignore[arg-type]
95
+ "agent-1",
96
+ name="Agent",
97
+ system_prompt="Help safely.",
98
+ unified_tools=[],
99
+ use_search=False,
100
+ )
101
+
102
+ self.assertNotIn("llm_model_settings", client.calls[0][2]["json"])
103
+
104
+
105
+ class AgentModelSettingsCommandTests(unittest.TestCase):
106
+ def _run_apply(
107
+ self,
108
+ payload: dict,
109
+ *,
110
+ agent_id: str | None = None,
111
+ dry_run: bool = False,
112
+ ) -> tuple[int, dict]:
113
+ with tempfile.TemporaryDirectory() as tmpdir:
114
+ payload_path = Path(tmpdir) / "agent.json"
115
+ payload_path.write_text(json.dumps(payload))
116
+ args = SimpleNamespace(
117
+ payload=str(payload_path),
118
+ agent_id=agent_id,
119
+ dry_run=dry_run,
120
+ note="settings regression",
121
+ out=None,
122
+ )
123
+ stdout = io.StringIO()
124
+ with contextlib.redirect_stdout(stdout):
125
+ status = agent_cmd.run_apply(args, object())
126
+ return status, json.loads(stdout.getvalue()) if stdout.getvalue() else {}
127
+
128
+ def test_apply_create_forwards_model_settings(self) -> None:
129
+ payload = {
130
+ "workspace_id": "workspace-1",
131
+ "name": "Agent",
132
+ "system_prompt": "Help safely.",
133
+ "llm_model": MODEL_ID,
134
+ "llm_model_settings": MODEL_SETTINGS,
135
+ }
136
+ history = [{"id": "history-1", "version_number": 1, "status": "draft"}]
137
+
138
+ with (
139
+ patch.object(
140
+ agent_cmd.agents_mod,
141
+ "create",
142
+ return_value={"id": "agent-1"},
143
+ ) as create,
144
+ patch.object(agent_cmd.agents_mod, "list_versions", return_value=history),
145
+ ):
146
+ status, _ = self._run_apply(payload)
147
+
148
+ self.assertEqual(status, 0)
149
+ self.assertEqual(
150
+ create.call_args.kwargs["llm_model_settings"],
151
+ MODEL_SETTINGS,
152
+ )
153
+
154
+ def test_apply_update_preserves_present_and_empty_settings(self) -> None:
155
+ history = [{"id": "history-2", "version_number": 2, "status": "draft"}]
156
+
157
+ for model_settings in (MODEL_SETTINGS, {}):
158
+ with self.subTest(model_settings=model_settings):
159
+ payload = {
160
+ "name": "Agent",
161
+ "system_prompt": "Help safely.",
162
+ "llm_model_settings": model_settings,
163
+ }
164
+ with (
165
+ patch.object(agent_cmd.agents_mod, "update") as update,
166
+ patch.object(
167
+ agent_cmd.agents_mod,
168
+ "list_versions",
169
+ return_value=history,
170
+ ),
171
+ ):
172
+ status, _ = self._run_apply(payload, agent_id="agent-1")
173
+
174
+ self.assertEqual(status, 0)
175
+ self.assertEqual(
176
+ update.call_args.kwargs["llm_model_settings"],
177
+ model_settings,
178
+ )
179
+
180
+ def test_apply_update_does_not_supply_omitted_settings(self) -> None:
181
+ payload = {
182
+ "name": "Agent",
183
+ "system_prompt": "Help safely.",
184
+ }
185
+ history = [{"id": "history-2", "version_number": 2, "status": "draft"}]
186
+
187
+ with (
188
+ patch.object(agent_cmd.agents_mod, "update") as update,
189
+ patch.object(agent_cmd.agents_mod, "list_versions", return_value=history),
190
+ ):
191
+ status, _ = self._run_apply(payload, agent_id="agent-1")
192
+
193
+ self.assertEqual(status, 0)
194
+ self.assertNotIn("llm_model_settings", update.call_args.kwargs)
195
+
196
+ def test_apply_dry_run_distinguishes_settings_states(self) -> None:
197
+ cases = (
198
+ (MODEL_SETTINGS, True, MODEL_SETTINGS),
199
+ ({}, True, {}),
200
+ (None, False, None),
201
+ )
202
+
203
+ for model_settings, expected_provided, expected_settings in cases:
204
+ with self.subTest(model_settings=model_settings):
205
+ payload: dict[str, object] = {
206
+ "name": "Agent",
207
+ "system_prompt": "Help safely.",
208
+ }
209
+ if expected_provided:
210
+ payload["llm_model_settings"] = model_settings
211
+
212
+ status, result = self._run_apply(
213
+ payload,
214
+ agent_id="agent-1",
215
+ dry_run=True,
216
+ )
217
+
218
+ self.assertEqual(status, 0)
219
+ self.assertEqual(
220
+ result["llm_model_settings_provided"],
221
+ expected_provided,
222
+ )
223
+ self.assertEqual(
224
+ result["llm_model_settings"],
225
+ expected_settings,
226
+ )
227
+
228
+
229
+ if __name__ == "__main__":
230
+ unittest.main()
@@ -26,7 +26,7 @@ wheels = [
26
26
 
27
27
  [[package]]
28
28
  name = "codeer-cli"
29
- version = "0.1.12"
29
+ version = "0.1.13"
30
30
  source = { editable = "." }
31
31
  dependencies = [
32
32
  { name = "httpx" },
File without changes
File without changes