mycode-sdk 0.9.4__tar.gz → 0.9.5__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.
- {mycode_sdk-0.9.4 → mycode_sdk-0.9.5}/PKG-INFO +6 -6
- {mycode_sdk-0.9.4 → mycode_sdk-0.9.5}/pyproject.toml +7 -7
- {mycode_sdk-0.9.4 → mycode_sdk-0.9.5}/src/mycode/agent.py +29 -41
- {mycode_sdk-0.9.4 → mycode_sdk-0.9.5}/src/mycode/models.py +14 -4
- {mycode_sdk-0.9.4 → mycode_sdk-0.9.5}/src/mycode/models_catalog.json +142 -114
- {mycode_sdk-0.9.4 → mycode_sdk-0.9.5}/src/mycode/providers/base.py +10 -5
- {mycode_sdk-0.9.4 → mycode_sdk-0.9.5}/src/mycode/providers/openai_chat.py +11 -16
- {mycode_sdk-0.9.4 → mycode_sdk-0.9.5}/src/mycode/providers/openai_responses.py +1 -1
- {mycode_sdk-0.9.4 → mycode_sdk-0.9.5}/src/mycode/session.py +10 -23
- {mycode_sdk-0.9.4 → mycode_sdk-0.9.5}/src/mycode/tools.py +10 -24
- {mycode_sdk-0.9.4 → mycode_sdk-0.9.5}/src/mycode/utils.py +0 -16
- {mycode_sdk-0.9.4 → mycode_sdk-0.9.5}/.gitignore +0 -0
- {mycode_sdk-0.9.4 → mycode_sdk-0.9.5}/LICENSE +0 -0
- {mycode_sdk-0.9.4 → mycode_sdk-0.9.5}/README.md +0 -0
- {mycode_sdk-0.9.4 → mycode_sdk-0.9.5}/src/mycode/__init__.py +0 -0
- {mycode_sdk-0.9.4 → mycode_sdk-0.9.5}/src/mycode/attachments.py +0 -0
- {mycode_sdk-0.9.4 → mycode_sdk-0.9.5}/src/mycode/compact.py +0 -0
- {mycode_sdk-0.9.4 → mycode_sdk-0.9.5}/src/mycode/hooks.py +0 -0
- {mycode_sdk-0.9.4 → mycode_sdk-0.9.5}/src/mycode/messages.py +0 -0
- {mycode_sdk-0.9.4 → mycode_sdk-0.9.5}/src/mycode/providers/__init__.py +0 -0
- {mycode_sdk-0.9.4 → mycode_sdk-0.9.5}/src/mycode/providers/anthropic_like.py +0 -0
- {mycode_sdk-0.9.4 → mycode_sdk-0.9.5}/src/mycode/providers/gemini.py +0 -0
- {mycode_sdk-0.9.4 → mycode_sdk-0.9.5}/src/mycode/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mycode-sdk
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.5
|
|
4
4
|
Summary: Lightweight Python SDK for building AI agents.
|
|
5
5
|
Project-URL: Homepage, https://github.com/legibet/mycode
|
|
6
6
|
Project-URL: Repository, https://github.com/legibet/mycode
|
|
@@ -18,11 +18,11 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.13
|
|
19
19
|
Classifier: Topic :: Software Development
|
|
20
20
|
Requires-Python: >=3.12
|
|
21
|
-
Requires-Dist: anthropic>=0.
|
|
22
|
-
Requires-Dist: google-genai>=2.
|
|
23
|
-
Requires-Dist: griffelib>=2.
|
|
24
|
-
Requires-Dist: openai>=2.
|
|
25
|
-
Requires-Dist: pydantic>=2.13.
|
|
21
|
+
Requires-Dist: anthropic>=0.116.0
|
|
22
|
+
Requires-Dist: google-genai>=2.11.0
|
|
23
|
+
Requires-Dist: griffelib>=2.1.0
|
|
24
|
+
Requires-Dist: openai>=2.45.0
|
|
25
|
+
Requires-Dist: pydantic>=2.13.4
|
|
26
26
|
Description-Content-Type: text/markdown
|
|
27
27
|
|
|
28
28
|
# mycode-sdk
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = ["hatchling"]
|
|
2
|
+
requires = ["hatchling>=1.31.0"]
|
|
3
3
|
build-backend = "hatchling.build"
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "mycode-sdk"
|
|
7
|
-
version = "0.9.
|
|
7
|
+
version = "0.9.5"
|
|
8
8
|
description = "Lightweight Python SDK for building AI agents."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.12"
|
|
@@ -23,11 +23,11 @@ classifiers = [
|
|
|
23
23
|
]
|
|
24
24
|
keywords = ["agent", "llm", "anthropic", "openai", "gemini", "sdk"]
|
|
25
25
|
dependencies = [
|
|
26
|
-
"anthropic>=0.
|
|
27
|
-
"google-genai>=2.
|
|
28
|
-
"griffelib>=2.
|
|
29
|
-
"openai>=2.
|
|
30
|
-
"pydantic>=2.13.
|
|
26
|
+
"anthropic>=0.116.0",
|
|
27
|
+
"google-genai>=2.11.0",
|
|
28
|
+
"griffelib>=2.1.0",
|
|
29
|
+
"openai>=2.45.0",
|
|
30
|
+
"pydantic>=2.13.4",
|
|
31
31
|
]
|
|
32
32
|
|
|
33
33
|
[project.urls]
|
|
@@ -152,16 +152,10 @@ class Agent:
|
|
|
152
152
|
# (e.g. bash spill files) live next to the session JSONL; without a
|
|
153
153
|
# session, fall back to a tempdir scoped to ``session_id``.
|
|
154
154
|
if session_dir is not None:
|
|
155
|
-
tool_output_dir = session_dir / self.session_id / "tool-output"
|
|
155
|
+
self.tool_output_dir = session_dir / self.session_id / "tool-output"
|
|
156
156
|
else:
|
|
157
|
-
tool_output_dir = Path(tempfile.gettempdir()) / "mycode" / self.session_id / "tool-output"
|
|
157
|
+
self.tool_output_dir = Path(tempfile.gettempdir()) / "mycode" / self.session_id / "tool-output"
|
|
158
158
|
self.tools = ToolExecutor(tools)
|
|
159
|
-
self.tool_ctx = ToolContext(
|
|
160
|
-
executor=self.tools,
|
|
161
|
-
cwd=self.cwd,
|
|
162
|
-
tool_output_dir=tool_output_dir,
|
|
163
|
-
supports_image_input=False,
|
|
164
|
-
)
|
|
165
159
|
|
|
166
160
|
self.refresh_capabilities(
|
|
167
161
|
max_tokens=max_tokens,
|
|
@@ -201,7 +195,6 @@ class Agent:
|
|
|
201
195
|
self.supports_reasoning: bool | None = meta.supports_reasoning
|
|
202
196
|
self.supports_image_input: bool = bool(meta.supports_image_input)
|
|
203
197
|
self.supports_pdf_input: bool = bool(meta.supports_pdf_input)
|
|
204
|
-
self.tool_ctx.supports_image_input = self.supports_image_input
|
|
205
198
|
|
|
206
199
|
def cancel(self) -> None:
|
|
207
200
|
"""Request cancellation of the in-flight turn."""
|
|
@@ -330,11 +323,11 @@ class Agent:
|
|
|
330
323
|
output = "\n".join([*output_parts, "error: cancelled"]) if output_parts else "error: cancelled"
|
|
331
324
|
yield self._error_done(tool_id, output)
|
|
332
325
|
return
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
326
|
+
|
|
327
|
+
try:
|
|
328
|
+
result = await task
|
|
329
|
+
except Exception as exc: # pragma: no cover - defensive
|
|
330
|
+
result = ToolExecutionResult(output=f"error: {exc}", is_error=True)
|
|
338
331
|
|
|
339
332
|
yield await self._finish_tool_call(tool_id, hook_ctx, result)
|
|
340
333
|
|
|
@@ -360,13 +353,11 @@ class Agent:
|
|
|
360
353
|
*,
|
|
361
354
|
emit: Callable[[str], None] | None = None,
|
|
362
355
|
) -> ToolContext:
|
|
363
|
-
"""Build a per-call ToolContext from the base context."""
|
|
364
|
-
|
|
365
356
|
return ToolContext(
|
|
366
357
|
executor=self.tools,
|
|
367
|
-
cwd=self.
|
|
368
|
-
tool_output_dir=self.
|
|
369
|
-
supports_image_input=self.
|
|
358
|
+
cwd=self.cwd,
|
|
359
|
+
tool_output_dir=self.tool_output_dir,
|
|
360
|
+
supports_image_input=self.supports_image_input,
|
|
370
361
|
tool_call_id=tool_id,
|
|
371
362
|
emit=emit,
|
|
372
363
|
)
|
|
@@ -402,15 +393,12 @@ class Agent:
|
|
|
402
393
|
|
|
403
394
|
provider_stream: AsyncIterator[ProviderStreamEvent] = adapter.stream_turn(request)
|
|
404
395
|
|
|
405
|
-
async def next_provider_event() -> ProviderStreamEvent:
|
|
406
|
-
return await anext(provider_stream)
|
|
407
|
-
|
|
408
396
|
try:
|
|
409
397
|
while True:
|
|
410
398
|
if self._cancel_event.is_set():
|
|
411
399
|
raise asyncio.CancelledError
|
|
412
400
|
|
|
413
|
-
self._provider_event_task = asyncio.
|
|
401
|
+
self._provider_event_task = asyncio.ensure_future(anext(provider_stream))
|
|
414
402
|
try:
|
|
415
403
|
yield await self._provider_event_task
|
|
416
404
|
except StopAsyncIteration:
|
|
@@ -529,7 +517,10 @@ class Agent:
|
|
|
529
517
|
adapter = get_provider_adapter(self.provider)
|
|
530
518
|
|
|
531
519
|
turn_number = 0
|
|
532
|
-
while
|
|
520
|
+
while True:
|
|
521
|
+
if self.max_turns is not None and turn_number >= self.max_turns:
|
|
522
|
+
yield Event("error", {"message": "max_turns reached"})
|
|
523
|
+
return
|
|
533
524
|
turn_number += 1
|
|
534
525
|
if self._cancel_event.is_set():
|
|
535
526
|
yield Event("error", {"message": "cancelled"})
|
|
@@ -632,13 +623,15 @@ class Agent:
|
|
|
632
623
|
|
|
633
624
|
total_tokens = meta.get("total_tokens")
|
|
634
625
|
if total_tokens:
|
|
635
|
-
|
|
636
|
-
"
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
626
|
+
yield Event(
|
|
627
|
+
"usage",
|
|
628
|
+
{
|
|
629
|
+
"total_tokens": total_tokens,
|
|
630
|
+
"model": meta.get("model") or self.model,
|
|
631
|
+
"provider": meta.get("provider") or self.provider,
|
|
632
|
+
"context_window": meta["context_window"],
|
|
633
|
+
},
|
|
634
|
+
)
|
|
642
635
|
|
|
643
636
|
tool_calls = [
|
|
644
637
|
block
|
|
@@ -665,11 +658,10 @@ class Agent:
|
|
|
665
658
|
)
|
|
666
659
|
)
|
|
667
660
|
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
return
|
|
661
|
+
if self._cancel_event.is_set():
|
|
662
|
+
# Skip remaining tool calls; the results collected so
|
|
663
|
+
# far are still persisted below.
|
|
664
|
+
break
|
|
673
665
|
|
|
674
666
|
tool_result_message = build_message("user", tool_results)
|
|
675
667
|
self.messages.append(tool_result_message)
|
|
@@ -693,11 +685,7 @@ class Agent:
|
|
|
693
685
|
)
|
|
694
686
|
|
|
695
687
|
if not tool_calls:
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
else:
|
|
699
|
-
yield Event("error", {"message": "max_turns reached"})
|
|
700
|
-
return
|
|
688
|
+
return
|
|
701
689
|
|
|
702
690
|
def run(
|
|
703
691
|
self,
|
|
@@ -66,16 +66,26 @@ def resolve_model_metadata(
|
|
|
66
66
|
*,
|
|
67
67
|
provider: str,
|
|
68
68
|
model: str,
|
|
69
|
-
|
|
69
|
+
context_window: int | None = None,
|
|
70
|
+
max_output_tokens: int | None = None,
|
|
71
|
+
supports_reasoning: bool | None = None,
|
|
72
|
+
supports_image_input: bool | None = None,
|
|
73
|
+
supports_pdf_input: bool | None = None,
|
|
70
74
|
) -> ModelMetadata:
|
|
71
75
|
"""Return catalog metadata for ``(provider, model)`` with non-None overrides layered on top.
|
|
72
76
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
so callers can apply their own fallback defaults.
|
|
77
|
+
Missing overrides and an absent catalog entry both leave the corresponding
|
|
78
|
+
fields at ``None`` so callers can apply their own fallback defaults.
|
|
76
79
|
"""
|
|
77
80
|
|
|
78
81
|
base = lookup_model_metadata(provider_type=provider, model=model) or ModelMetadata(provider=provider, model=model)
|
|
82
|
+
overrides = {
|
|
83
|
+
"context_window": context_window,
|
|
84
|
+
"max_output_tokens": max_output_tokens,
|
|
85
|
+
"supports_reasoning": supports_reasoning,
|
|
86
|
+
"supports_image_input": supports_image_input,
|
|
87
|
+
"supports_pdf_input": supports_pdf_input,
|
|
88
|
+
}
|
|
79
89
|
return replace(base, **{k: v for k, v in overrides.items() if v is not None})
|
|
80
90
|
|
|
81
91
|
|
|
@@ -1,47 +1,5 @@
|
|
|
1
1
|
{
|
|
2
2
|
"anthropic": {
|
|
3
|
-
"claude-3-5-sonnet-20240620": {
|
|
4
|
-
"context_window": 200000,
|
|
5
|
-
"max_output_tokens": 8192,
|
|
6
|
-
"supports_image_input": true,
|
|
7
|
-
"supports_pdf_input": true,
|
|
8
|
-
"supports_reasoning": false
|
|
9
|
-
},
|
|
10
|
-
"claude-3-5-sonnet-20241022": {
|
|
11
|
-
"context_window": 200000,
|
|
12
|
-
"max_output_tokens": 8192,
|
|
13
|
-
"supports_image_input": true,
|
|
14
|
-
"supports_pdf_input": true,
|
|
15
|
-
"supports_reasoning": false
|
|
16
|
-
},
|
|
17
|
-
"claude-3-7-sonnet-20250219": {
|
|
18
|
-
"context_window": 200000,
|
|
19
|
-
"max_output_tokens": 64000,
|
|
20
|
-
"supports_image_input": true,
|
|
21
|
-
"supports_pdf_input": true,
|
|
22
|
-
"supports_reasoning": true
|
|
23
|
-
},
|
|
24
|
-
"claude-3-haiku-20240307": {
|
|
25
|
-
"context_window": 200000,
|
|
26
|
-
"max_output_tokens": 4096,
|
|
27
|
-
"supports_image_input": true,
|
|
28
|
-
"supports_pdf_input": true,
|
|
29
|
-
"supports_reasoning": false
|
|
30
|
-
},
|
|
31
|
-
"claude-3-opus-20240229": {
|
|
32
|
-
"context_window": 200000,
|
|
33
|
-
"max_output_tokens": 4096,
|
|
34
|
-
"supports_image_input": true,
|
|
35
|
-
"supports_pdf_input": true,
|
|
36
|
-
"supports_reasoning": false
|
|
37
|
-
},
|
|
38
|
-
"claude-3-sonnet-20240229": {
|
|
39
|
-
"context_window": 200000,
|
|
40
|
-
"max_output_tokens": 4096,
|
|
41
|
-
"supports_image_input": true,
|
|
42
|
-
"supports_pdf_input": true,
|
|
43
|
-
"supports_reasoning": false
|
|
44
|
-
},
|
|
45
3
|
"claude-fable-5": {
|
|
46
4
|
"context_window": 1000000,
|
|
47
5
|
"max_output_tokens": 128000,
|
|
@@ -63,13 +21,6 @@
|
|
|
63
21
|
"supports_pdf_input": true,
|
|
64
22
|
"supports_reasoning": true
|
|
65
23
|
},
|
|
66
|
-
"claude-opus-4-0": {
|
|
67
|
-
"context_window": 200000,
|
|
68
|
-
"max_output_tokens": 32000,
|
|
69
|
-
"supports_image_input": true,
|
|
70
|
-
"supports_pdf_input": true,
|
|
71
|
-
"supports_reasoning": true
|
|
72
|
-
},
|
|
73
24
|
"claude-opus-4-1": {
|
|
74
25
|
"context_window": 200000,
|
|
75
26
|
"max_output_tokens": 32000,
|
|
@@ -84,13 +35,6 @@
|
|
|
84
35
|
"supports_pdf_input": true,
|
|
85
36
|
"supports_reasoning": true
|
|
86
37
|
},
|
|
87
|
-
"claude-opus-4-20250514": {
|
|
88
|
-
"context_window": 200000,
|
|
89
|
-
"max_output_tokens": 32000,
|
|
90
|
-
"supports_image_input": true,
|
|
91
|
-
"supports_pdf_input": true,
|
|
92
|
-
"supports_reasoning": true
|
|
93
|
-
},
|
|
94
38
|
"claude-opus-4-5": {
|
|
95
39
|
"context_window": 200000,
|
|
96
40
|
"max_output_tokens": 64000,
|
|
@@ -126,29 +70,15 @@
|
|
|
126
70
|
"supports_pdf_input": true,
|
|
127
71
|
"supports_reasoning": true
|
|
128
72
|
},
|
|
129
|
-
"claude-sonnet-4-0": {
|
|
130
|
-
"context_window": 200000,
|
|
131
|
-
"max_output_tokens": 64000,
|
|
132
|
-
"supports_image_input": true,
|
|
133
|
-
"supports_pdf_input": true,
|
|
134
|
-
"supports_reasoning": true
|
|
135
|
-
},
|
|
136
|
-
"claude-sonnet-4-20250514": {
|
|
137
|
-
"context_window": 200000,
|
|
138
|
-
"max_output_tokens": 64000,
|
|
139
|
-
"supports_image_input": true,
|
|
140
|
-
"supports_pdf_input": true,
|
|
141
|
-
"supports_reasoning": true
|
|
142
|
-
},
|
|
143
73
|
"claude-sonnet-4-5": {
|
|
144
|
-
"context_window":
|
|
74
|
+
"context_window": 1000000,
|
|
145
75
|
"max_output_tokens": 64000,
|
|
146
76
|
"supports_image_input": true,
|
|
147
77
|
"supports_pdf_input": true,
|
|
148
78
|
"supports_reasoning": true
|
|
149
79
|
},
|
|
150
80
|
"claude-sonnet-4-5-20250929": {
|
|
151
|
-
"context_window":
|
|
81
|
+
"context_window": 1000000,
|
|
152
82
|
"max_output_tokens": 64000,
|
|
153
83
|
"supports_image_input": true,
|
|
154
84
|
"supports_pdf_input": true,
|
|
@@ -156,7 +86,7 @@
|
|
|
156
86
|
},
|
|
157
87
|
"claude-sonnet-4-6": {
|
|
158
88
|
"context_window": 1000000,
|
|
159
|
-
"max_output_tokens":
|
|
89
|
+
"max_output_tokens": 128000,
|
|
160
90
|
"supports_image_input": true,
|
|
161
91
|
"supports_pdf_input": true,
|
|
162
92
|
"supports_reasoning": true
|
|
@@ -340,6 +270,13 @@
|
|
|
340
270
|
"supports_pdf_input": true,
|
|
341
271
|
"supports_reasoning": true
|
|
342
272
|
},
|
|
273
|
+
"gemini-omni-flash-preview": {
|
|
274
|
+
"context_window": 131072,
|
|
275
|
+
"max_output_tokens": 65536,
|
|
276
|
+
"supports_image_input": true,
|
|
277
|
+
"supports_pdf_input": false,
|
|
278
|
+
"supports_reasoning": true
|
|
279
|
+
},
|
|
343
280
|
"gemma-4-26b-a4b-it": {
|
|
344
281
|
"context_window": 262144,
|
|
345
282
|
"max_output_tokens": 32768,
|
|
@@ -724,6 +661,34 @@
|
|
|
724
661
|
"supports_pdf_input": true,
|
|
725
662
|
"supports_reasoning": true
|
|
726
663
|
},
|
|
664
|
+
"gpt-5.6": {
|
|
665
|
+
"context_window": 1050000,
|
|
666
|
+
"max_output_tokens": 128000,
|
|
667
|
+
"supports_image_input": true,
|
|
668
|
+
"supports_pdf_input": true,
|
|
669
|
+
"supports_reasoning": true
|
|
670
|
+
},
|
|
671
|
+
"gpt-5.6-luna": {
|
|
672
|
+
"context_window": 1050000,
|
|
673
|
+
"max_output_tokens": 128000,
|
|
674
|
+
"supports_image_input": true,
|
|
675
|
+
"supports_pdf_input": true,
|
|
676
|
+
"supports_reasoning": true
|
|
677
|
+
},
|
|
678
|
+
"gpt-5.6-sol": {
|
|
679
|
+
"context_window": 1050000,
|
|
680
|
+
"max_output_tokens": 128000,
|
|
681
|
+
"supports_image_input": true,
|
|
682
|
+
"supports_pdf_input": true,
|
|
683
|
+
"supports_reasoning": true
|
|
684
|
+
},
|
|
685
|
+
"gpt-5.6-terra": {
|
|
686
|
+
"context_window": 1050000,
|
|
687
|
+
"max_output_tokens": 128000,
|
|
688
|
+
"supports_image_input": true,
|
|
689
|
+
"supports_pdf_input": true,
|
|
690
|
+
"supports_reasoning": true
|
|
691
|
+
},
|
|
727
692
|
"gpt-image-1": {
|
|
728
693
|
"context_window": 0,
|
|
729
694
|
"max_output_tokens": 0,
|
|
@@ -838,21 +803,21 @@
|
|
|
838
803
|
"supports_pdf_input": false,
|
|
839
804
|
"supports_reasoning": false
|
|
840
805
|
},
|
|
841
|
-
"aion-labs/aion-
|
|
806
|
+
"aion-labs/aion-2.0": {
|
|
842
807
|
"context_window": 131072,
|
|
843
808
|
"max_output_tokens": 32768,
|
|
844
809
|
"supports_image_input": false,
|
|
845
810
|
"supports_pdf_input": false,
|
|
846
811
|
"supports_reasoning": true
|
|
847
812
|
},
|
|
848
|
-
"aion-labs/aion-
|
|
813
|
+
"aion-labs/aion-3.0": {
|
|
849
814
|
"context_window": 131072,
|
|
850
815
|
"max_output_tokens": 32768,
|
|
851
816
|
"supports_image_input": false,
|
|
852
817
|
"supports_pdf_input": false,
|
|
853
818
|
"supports_reasoning": true
|
|
854
819
|
},
|
|
855
|
-
"aion-labs/aion-
|
|
820
|
+
"aion-labs/aion-3.0-mini": {
|
|
856
821
|
"context_window": 131072,
|
|
857
822
|
"max_output_tokens": 32768,
|
|
858
823
|
"supports_image_input": false,
|
|
@@ -922,6 +887,13 @@
|
|
|
922
887
|
"supports_pdf_input": false,
|
|
923
888
|
"supports_reasoning": false
|
|
924
889
|
},
|
|
890
|
+
"anthropic/claude-fable-5": {
|
|
891
|
+
"context_window": 1000000,
|
|
892
|
+
"max_output_tokens": 128000,
|
|
893
|
+
"supports_image_input": true,
|
|
894
|
+
"supports_pdf_input": true,
|
|
895
|
+
"supports_reasoning": true
|
|
896
|
+
},
|
|
925
897
|
"anthropic/claude-haiku-4.5": {
|
|
926
898
|
"context_window": 200000,
|
|
927
899
|
"max_output_tokens": 64000,
|
|
@@ -1196,7 +1168,7 @@
|
|
|
1196
1168
|
"supports_reasoning": true
|
|
1197
1169
|
},
|
|
1198
1170
|
"deepseek/deepseek-v4-flash": {
|
|
1199
|
-
"context_window":
|
|
1171
|
+
"context_window": 1048576,
|
|
1200
1172
|
"max_output_tokens": 65536,
|
|
1201
1173
|
"supports_image_input": false,
|
|
1202
1174
|
"supports_pdf_input": false,
|
|
@@ -1230,13 +1202,6 @@
|
|
|
1230
1202
|
"supports_pdf_input": true,
|
|
1231
1203
|
"supports_reasoning": true
|
|
1232
1204
|
},
|
|
1233
|
-
"google/gemini-2.5-flash-lite-preview-09-2025": {
|
|
1234
|
-
"context_window": 1048576,
|
|
1235
|
-
"max_output_tokens": 65535,
|
|
1236
|
-
"supports_image_input": true,
|
|
1237
|
-
"supports_pdf_input": true,
|
|
1238
|
-
"supports_reasoning": true
|
|
1239
|
-
},
|
|
1240
1205
|
"google/gemini-2.5-pro": {
|
|
1241
1206
|
"context_window": 1048576,
|
|
1242
1207
|
"max_output_tokens": 65536,
|
|
@@ -1482,13 +1447,6 @@
|
|
|
1482
1447
|
"supports_pdf_input": false,
|
|
1483
1448
|
"supports_reasoning": false
|
|
1484
1449
|
},
|
|
1485
|
-
"liquid/lfm-2-24b-a2b": {
|
|
1486
|
-
"context_window": 32768,
|
|
1487
|
-
"max_output_tokens": 32768,
|
|
1488
|
-
"supports_image_input": false,
|
|
1489
|
-
"supports_pdf_input": false,
|
|
1490
|
-
"supports_reasoning": false
|
|
1491
|
-
},
|
|
1492
1450
|
"liquid/lfm-2.5-1.2b-instruct:free": {
|
|
1493
1451
|
"context_window": 32768,
|
|
1494
1452
|
"max_output_tokens": 32768,
|
|
@@ -1546,8 +1504,8 @@
|
|
|
1546
1504
|
"supports_reasoning": false
|
|
1547
1505
|
},
|
|
1548
1506
|
"meta-llama/llama-3.2-3b-instruct": {
|
|
1549
|
-
"context_window":
|
|
1550
|
-
"max_output_tokens":
|
|
1507
|
+
"context_window": 131072,
|
|
1508
|
+
"max_output_tokens": 131072,
|
|
1551
1509
|
"supports_image_input": false,
|
|
1552
1510
|
"supports_pdf_input": false,
|
|
1553
1511
|
"supports_reasoning": false
|
|
@@ -1623,8 +1581,8 @@
|
|
|
1623
1581
|
"supports_reasoning": true
|
|
1624
1582
|
},
|
|
1625
1583
|
"minimax/minimax-m2": {
|
|
1626
|
-
"context_window":
|
|
1627
|
-
"max_output_tokens":
|
|
1584
|
+
"context_window": 204800,
|
|
1585
|
+
"max_output_tokens": 131072,
|
|
1628
1586
|
"supports_image_input": false,
|
|
1629
1587
|
"supports_pdf_input": false,
|
|
1630
1588
|
"supports_reasoning": true
|
|
@@ -1637,8 +1595,8 @@
|
|
|
1637
1595
|
"supports_reasoning": false
|
|
1638
1596
|
},
|
|
1639
1597
|
"minimax/minimax-m2.1": {
|
|
1640
|
-
"context_window":
|
|
1641
|
-
"max_output_tokens":
|
|
1598
|
+
"context_window": 204800,
|
|
1599
|
+
"max_output_tokens": 131072,
|
|
1642
1600
|
"supports_image_input": false,
|
|
1643
1601
|
"supports_pdf_input": false,
|
|
1644
1602
|
"supports_reasoning": true
|
|
@@ -1813,7 +1771,7 @@
|
|
|
1813
1771
|
},
|
|
1814
1772
|
"moonshotai/kimi-k2-thinking": {
|
|
1815
1773
|
"context_window": 262144,
|
|
1816
|
-
"max_output_tokens":
|
|
1774
|
+
"max_output_tokens": 100352,
|
|
1817
1775
|
"supports_image_input": false,
|
|
1818
1776
|
"supports_pdf_input": false,
|
|
1819
1777
|
"supports_reasoning": true
|
|
@@ -1834,7 +1792,7 @@
|
|
|
1834
1792
|
},
|
|
1835
1793
|
"moonshotai/kimi-k2.7-code": {
|
|
1836
1794
|
"context_window": 262144,
|
|
1837
|
-
"max_output_tokens":
|
|
1795
|
+
"max_output_tokens": 262144,
|
|
1838
1796
|
"supports_image_input": true,
|
|
1839
1797
|
"supports_pdf_input": false,
|
|
1840
1798
|
"supports_reasoning": true
|
|
@@ -1853,6 +1811,13 @@
|
|
|
1853
1811
|
"supports_pdf_input": false,
|
|
1854
1812
|
"supports_reasoning": false
|
|
1855
1813
|
},
|
|
1814
|
+
"nex-agi/nex-n2-mini": {
|
|
1815
|
+
"context_window": 262144,
|
|
1816
|
+
"max_output_tokens": 262144,
|
|
1817
|
+
"supports_image_input": true,
|
|
1818
|
+
"supports_pdf_input": false,
|
|
1819
|
+
"supports_reasoning": true
|
|
1820
|
+
},
|
|
1856
1821
|
"nex-agi/nex-n2-pro": {
|
|
1857
1822
|
"context_window": 262144,
|
|
1858
1823
|
"max_output_tokens": 262144,
|
|
@@ -2280,6 +2245,48 @@
|
|
|
2280
2245
|
"supports_pdf_input": true,
|
|
2281
2246
|
"supports_reasoning": true
|
|
2282
2247
|
},
|
|
2248
|
+
"openai/gpt-5.6-luna": {
|
|
2249
|
+
"context_window": 1050000,
|
|
2250
|
+
"max_output_tokens": 128000,
|
|
2251
|
+
"supports_image_input": true,
|
|
2252
|
+
"supports_pdf_input": true,
|
|
2253
|
+
"supports_reasoning": true
|
|
2254
|
+
},
|
|
2255
|
+
"openai/gpt-5.6-luna-pro": {
|
|
2256
|
+
"context_window": 1050000,
|
|
2257
|
+
"max_output_tokens": 128000,
|
|
2258
|
+
"supports_image_input": true,
|
|
2259
|
+
"supports_pdf_input": true,
|
|
2260
|
+
"supports_reasoning": true
|
|
2261
|
+
},
|
|
2262
|
+
"openai/gpt-5.6-sol": {
|
|
2263
|
+
"context_window": 1050000,
|
|
2264
|
+
"max_output_tokens": 128000,
|
|
2265
|
+
"supports_image_input": true,
|
|
2266
|
+
"supports_pdf_input": true,
|
|
2267
|
+
"supports_reasoning": true
|
|
2268
|
+
},
|
|
2269
|
+
"openai/gpt-5.6-sol-pro": {
|
|
2270
|
+
"context_window": 1050000,
|
|
2271
|
+
"max_output_tokens": 128000,
|
|
2272
|
+
"supports_image_input": true,
|
|
2273
|
+
"supports_pdf_input": true,
|
|
2274
|
+
"supports_reasoning": true
|
|
2275
|
+
},
|
|
2276
|
+
"openai/gpt-5.6-terra": {
|
|
2277
|
+
"context_window": 1050000,
|
|
2278
|
+
"max_output_tokens": 128000,
|
|
2279
|
+
"supports_image_input": true,
|
|
2280
|
+
"supports_pdf_input": true,
|
|
2281
|
+
"supports_reasoning": true
|
|
2282
|
+
},
|
|
2283
|
+
"openai/gpt-5.6-terra-pro": {
|
|
2284
|
+
"context_window": 1050000,
|
|
2285
|
+
"max_output_tokens": 128000,
|
|
2286
|
+
"supports_image_input": true,
|
|
2287
|
+
"supports_pdf_input": true,
|
|
2288
|
+
"supports_reasoning": true
|
|
2289
|
+
},
|
|
2283
2290
|
"openai/gpt-audio": {
|
|
2284
2291
|
"context_window": 128000,
|
|
2285
2292
|
"max_output_tokens": 16384,
|
|
@@ -2497,14 +2504,14 @@
|
|
|
2497
2504
|
"supports_pdf_input": false,
|
|
2498
2505
|
"supports_reasoning": true
|
|
2499
2506
|
},
|
|
2500
|
-
"poolside/laguna-xs.
|
|
2507
|
+
"poolside/laguna-xs-2.1": {
|
|
2501
2508
|
"context_window": 262144,
|
|
2502
2509
|
"max_output_tokens": 32768,
|
|
2503
2510
|
"supports_image_input": false,
|
|
2504
2511
|
"supports_pdf_input": false,
|
|
2505
2512
|
"supports_reasoning": true
|
|
2506
2513
|
},
|
|
2507
|
-
"poolside/laguna-xs.
|
|
2514
|
+
"poolside/laguna-xs-2.1:free": {
|
|
2508
2515
|
"context_window": 262144,
|
|
2509
2516
|
"max_output_tokens": 32768,
|
|
2510
2517
|
"supports_image_input": false,
|
|
@@ -2603,8 +2610,8 @@
|
|
|
2603
2610
|
"supports_reasoning": false
|
|
2604
2611
|
},
|
|
2605
2612
|
"qwen/qwen3-30b-a3b-thinking-2507": {
|
|
2606
|
-
"context_window":
|
|
2607
|
-
"max_output_tokens":
|
|
2613
|
+
"context_window": 81920,
|
|
2614
|
+
"max_output_tokens": 32768,
|
|
2608
2615
|
"supports_image_input": false,
|
|
2609
2616
|
"supports_pdf_input": false,
|
|
2610
2617
|
"supports_reasoning": true
|
|
@@ -2617,7 +2624,7 @@
|
|
|
2617
2624
|
"supports_reasoning": true
|
|
2618
2625
|
},
|
|
2619
2626
|
"qwen/qwen3-8b": {
|
|
2620
|
-
"context_window":
|
|
2627
|
+
"context_window": 131072,
|
|
2621
2628
|
"max_output_tokens": 8192,
|
|
2622
2629
|
"supports_image_input": false,
|
|
2623
2630
|
"supports_pdf_input": false,
|
|
@@ -2931,15 +2938,15 @@
|
|
|
2931
2938
|
"supports_pdf_input": false,
|
|
2932
2939
|
"supports_reasoning": true
|
|
2933
2940
|
},
|
|
2934
|
-
"
|
|
2941
|
+
"tencent/hunyuan-a13b-instruct": {
|
|
2935
2942
|
"context_window": 131072,
|
|
2936
2943
|
"max_output_tokens": 131072,
|
|
2937
2944
|
"supports_image_input": false,
|
|
2938
2945
|
"supports_pdf_input": false,
|
|
2939
2946
|
"supports_reasoning": true
|
|
2940
2947
|
},
|
|
2941
|
-
"tencent/
|
|
2942
|
-
"context_window":
|
|
2948
|
+
"tencent/hy3": {
|
|
2949
|
+
"context_window": 262144,
|
|
2943
2950
|
"max_output_tokens": 131072,
|
|
2944
2951
|
"supports_image_input": false,
|
|
2945
2952
|
"supports_pdf_input": false,
|
|
@@ -2952,6 +2959,13 @@
|
|
|
2952
2959
|
"supports_pdf_input": false,
|
|
2953
2960
|
"supports_reasoning": true
|
|
2954
2961
|
},
|
|
2962
|
+
"tencent/hy3:free": {
|
|
2963
|
+
"context_window": 262144,
|
|
2964
|
+
"max_output_tokens": 262144,
|
|
2965
|
+
"supports_image_input": false,
|
|
2966
|
+
"supports_pdf_input": false,
|
|
2967
|
+
"supports_reasoning": true
|
|
2968
|
+
},
|
|
2955
2969
|
"thedrummer/cydonia-24b-v4.1": {
|
|
2956
2970
|
"context_window": 131072,
|
|
2957
2971
|
"max_output_tokens": 131072,
|
|
@@ -2960,8 +2974,8 @@
|
|
|
2960
2974
|
"supports_reasoning": false
|
|
2961
2975
|
},
|
|
2962
2976
|
"thedrummer/rocinante-12b": {
|
|
2963
|
-
"context_window":
|
|
2964
|
-
"max_output_tokens":
|
|
2977
|
+
"context_window": 65536,
|
|
2978
|
+
"max_output_tokens": 65536,
|
|
2965
2979
|
"supports_image_input": false,
|
|
2966
2980
|
"supports_pdf_input": false,
|
|
2967
2981
|
"supports_reasoning": false
|
|
@@ -3022,15 +3036,22 @@
|
|
|
3022
3036
|
"supports_pdf_input": true,
|
|
3023
3037
|
"supports_reasoning": true
|
|
3024
3038
|
},
|
|
3039
|
+
"x-ai/grok-4.5": {
|
|
3040
|
+
"context_window": 500000,
|
|
3041
|
+
"max_output_tokens": 500000,
|
|
3042
|
+
"supports_image_input": true,
|
|
3043
|
+
"supports_pdf_input": true,
|
|
3044
|
+
"supports_reasoning": true
|
|
3045
|
+
},
|
|
3025
3046
|
"x-ai/grok-build-0.1": {
|
|
3026
3047
|
"context_window": 256000,
|
|
3027
3048
|
"max_output_tokens": 256000,
|
|
3028
3049
|
"supports_image_input": true,
|
|
3029
|
-
"supports_pdf_input":
|
|
3050
|
+
"supports_pdf_input": true,
|
|
3030
3051
|
"supports_reasoning": true
|
|
3031
3052
|
},
|
|
3032
3053
|
"xiaomi/mimo-v2.5": {
|
|
3033
|
-
"context_window":
|
|
3054
|
+
"context_window": 1048576,
|
|
3034
3055
|
"max_output_tokens": 131072,
|
|
3035
3056
|
"supports_image_input": true,
|
|
3036
3057
|
"supports_pdf_input": false,
|
|
@@ -3107,15 +3128,15 @@
|
|
|
3107
3128
|
"supports_reasoning": true
|
|
3108
3129
|
},
|
|
3109
3130
|
"z-ai/glm-5.1": {
|
|
3110
|
-
"context_window":
|
|
3131
|
+
"context_window": 200000,
|
|
3111
3132
|
"max_output_tokens": 128000,
|
|
3112
3133
|
"supports_image_input": false,
|
|
3113
3134
|
"supports_pdf_input": false,
|
|
3114
3135
|
"supports_reasoning": true
|
|
3115
3136
|
},
|
|
3116
3137
|
"z-ai/glm-5.2": {
|
|
3117
|
-
"context_window":
|
|
3118
|
-
"max_output_tokens":
|
|
3138
|
+
"context_window": 101376,
|
|
3139
|
+
"max_output_tokens": 101376,
|
|
3119
3140
|
"supports_image_input": false,
|
|
3120
3141
|
"supports_pdf_input": false,
|
|
3121
3142
|
"supports_reasoning": true
|
|
@@ -3189,6 +3210,13 @@
|
|
|
3189
3210
|
"supports_image_input": true,
|
|
3190
3211
|
"supports_pdf_input": true,
|
|
3191
3212
|
"supports_reasoning": true
|
|
3213
|
+
},
|
|
3214
|
+
"~x-ai/grok-latest": {
|
|
3215
|
+
"context_window": 500000,
|
|
3216
|
+
"max_output_tokens": 1000000,
|
|
3217
|
+
"supports_image_input": true,
|
|
3218
|
+
"supports_pdf_input": true,
|
|
3219
|
+
"supports_reasoning": true
|
|
3192
3220
|
}
|
|
3193
3221
|
},
|
|
3194
3222
|
"zai": {
|
|
@@ -7,6 +7,7 @@ before sending a request upstream.
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
+
import json
|
|
10
11
|
import os
|
|
11
12
|
from abc import ABC, abstractmethod
|
|
12
13
|
from collections.abc import AsyncIterator
|
|
@@ -16,7 +17,6 @@ from typing import Any
|
|
|
16
17
|
from mycode.attachments import unsupported_attachment_block
|
|
17
18
|
from mycode.compact import apply_compact_replay
|
|
18
19
|
from mycode.messages import ConversationMessage, build_message, text_block, tool_result_block
|
|
19
|
-
from mycode.utils import parse_tool_arguments
|
|
20
20
|
|
|
21
21
|
DEFAULT_REQUEST_TIMEOUT = 300.0
|
|
22
22
|
|
|
@@ -83,10 +83,15 @@ def parse_tool_call_input(raw_arguments: str) -> tuple[dict[str, Any], dict[str,
|
|
|
83
83
|
``raw_arguments`` so it stays inspectable in ``block.meta.native``.
|
|
84
84
|
"""
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
86
|
+
if not raw_arguments.strip():
|
|
87
|
+
return {}, {}
|
|
88
|
+
try:
|
|
89
|
+
parsed = json.loads(raw_arguments)
|
|
90
|
+
except json.JSONDecodeError:
|
|
91
|
+
parsed = None
|
|
92
|
+
if isinstance(parsed, dict):
|
|
93
|
+
return parsed, {}
|
|
94
|
+
return {}, {"raw_arguments": raw_arguments}
|
|
90
95
|
|
|
91
96
|
|
|
92
97
|
class ProviderAdapter(ABC):
|
|
@@ -29,7 +29,6 @@ from mycode.utils import omit_none
|
|
|
29
29
|
class _ChatToolCallState:
|
|
30
30
|
"""Accumulate one streamed tool call from chat-completions deltas."""
|
|
31
31
|
|
|
32
|
-
index: int
|
|
33
32
|
tool_id: str | None = None
|
|
34
33
|
name: str = ""
|
|
35
34
|
arguments_text: str = ""
|
|
@@ -93,7 +92,7 @@ class OpenAIChatAdapter(ProviderAdapter):
|
|
|
93
92
|
|
|
94
93
|
for tool_call in delta.tool_calls or []:
|
|
95
94
|
index = tool_call.index or 0
|
|
96
|
-
state = tool_calls.setdefault(index, _ChatToolCallState(
|
|
95
|
+
state = tool_calls.setdefault(index, _ChatToolCallState())
|
|
97
96
|
if tool_call.id:
|
|
98
97
|
state.tool_id = tool_call.id
|
|
99
98
|
function = tool_call.function
|
|
@@ -184,27 +183,26 @@ class OpenAIChatAdapter(ProviderAdapter):
|
|
|
184
183
|
if role == "user":
|
|
185
184
|
payload_messages: list[dict[str, Any]] = []
|
|
186
185
|
has_media = any(block.get("type") in {"image", "document"} for block in blocks)
|
|
186
|
+
user_content: str | list[dict[str, Any]]
|
|
187
187
|
if has_media:
|
|
188
|
-
|
|
188
|
+
media_parts: list[dict[str, Any]] = []
|
|
189
189
|
for block in blocks:
|
|
190
190
|
block_type = block.get("type")
|
|
191
191
|
if block_type == "text":
|
|
192
192
|
text = str(block.get("text") or "")
|
|
193
193
|
if text:
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
if block_type == "image":
|
|
194
|
+
media_parts.append({"type": "text", "text": text})
|
|
195
|
+
elif block_type == "image":
|
|
197
196
|
mime_type, data = load_image_block_payload(block)
|
|
198
|
-
|
|
197
|
+
media_parts.append(
|
|
199
198
|
{
|
|
200
199
|
"type": "image_url",
|
|
201
200
|
"image_url": {"url": f"data:{mime_type};base64,{data}"},
|
|
202
201
|
}
|
|
203
202
|
)
|
|
204
|
-
|
|
205
|
-
if block_type == "document":
|
|
203
|
+
elif block_type == "document":
|
|
206
204
|
mime_type, data, name = load_document_block_payload(block)
|
|
207
|
-
|
|
205
|
+
media_parts.append(
|
|
208
206
|
{
|
|
209
207
|
"type": "file",
|
|
210
208
|
"file": {
|
|
@@ -213,16 +211,13 @@ class OpenAIChatAdapter(ProviderAdapter):
|
|
|
213
211
|
},
|
|
214
212
|
}
|
|
215
213
|
)
|
|
216
|
-
|
|
217
|
-
user_content = None
|
|
214
|
+
user_content = media_parts
|
|
218
215
|
else:
|
|
219
|
-
|
|
216
|
+
user_content = "\n".join(
|
|
220
217
|
str(block.get("text") or "")
|
|
221
218
|
for block in blocks
|
|
222
219
|
if block.get("type") == "text" and block.get("text")
|
|
223
|
-
|
|
224
|
-
text = "\n".join(text_parts)
|
|
225
|
-
user_content = text or None
|
|
220
|
+
)
|
|
226
221
|
|
|
227
222
|
if user_content:
|
|
228
223
|
payload_messages.append({"role": "user", "content": user_content})
|
|
@@ -32,7 +32,7 @@ class OpenAIResponsesAdapter(ProviderAdapter):
|
|
|
32
32
|
label = "OpenAI Responses"
|
|
33
33
|
default_base_url = "https://api.openai.com/v1"
|
|
34
34
|
env_api_key_names = ("OPENAI_API_KEY",)
|
|
35
|
-
default_models = ("gpt-5.5", "gpt-5.
|
|
35
|
+
default_models = ("gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna")
|
|
36
36
|
supports_reasoning_effort = True
|
|
37
37
|
|
|
38
38
|
@override
|
|
@@ -15,7 +15,7 @@ import asyncio
|
|
|
15
15
|
import json
|
|
16
16
|
import os
|
|
17
17
|
import shutil
|
|
18
|
-
from dataclasses import
|
|
18
|
+
from dataclasses import dataclass
|
|
19
19
|
from datetime import UTC, datetime
|
|
20
20
|
from pathlib import Path
|
|
21
21
|
from typing import TypedDict, cast
|
|
@@ -23,6 +23,9 @@ from typing import TypedDict, cast
|
|
|
23
23
|
from mycode.messages import ConversationMessage, flatten_message_text
|
|
24
24
|
|
|
25
25
|
DEFAULT_SESSION_TITLE = "New chat"
|
|
26
|
+
# Session meta keeps only these stable fields. Per-turn state like provider /
|
|
27
|
+
# model / api_base lives on each ConversationMessage and would drift after
|
|
28
|
+
# ``/model`` switches.
|
|
26
29
|
META_KEYS = ("cwd", "title", "created_at", "updated_at")
|
|
27
30
|
|
|
28
31
|
|
|
@@ -67,20 +70,6 @@ def apply_rewind(messages: list[ConversationMessage]) -> list[ConversationMessag
|
|
|
67
70
|
# ---------------------------------------------------------------------
|
|
68
71
|
|
|
69
72
|
|
|
70
|
-
@dataclass
|
|
71
|
-
class SessionMeta:
|
|
72
|
-
"""Session metadata persisted to meta.json.
|
|
73
|
-
|
|
74
|
-
Excludes per-turn state like provider / model / api_base — those live on
|
|
75
|
-
each ConversationMessage and would drift after ``/model`` switches.
|
|
76
|
-
"""
|
|
77
|
-
|
|
78
|
-
cwd: str
|
|
79
|
-
title: str
|
|
80
|
-
created_at: str
|
|
81
|
-
updated_at: str
|
|
82
|
-
|
|
83
|
-
|
|
84
73
|
SessionMetaDict = dict[str, object]
|
|
85
74
|
SessionIndex = dict[str, SessionMetaDict]
|
|
86
75
|
|
|
@@ -184,14 +173,12 @@ class SessionStore:
|
|
|
184
173
|
"""Create the on-disk session directory with a fresh meta.json."""
|
|
185
174
|
|
|
186
175
|
now = _now()
|
|
187
|
-
meta =
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
)
|
|
194
|
-
)
|
|
176
|
+
meta: SessionMetaDict = {
|
|
177
|
+
"cwd": os.path.abspath(cwd),
|
|
178
|
+
"title": DEFAULT_SESSION_TITLE,
|
|
179
|
+
"created_at": now,
|
|
180
|
+
"updated_at": now,
|
|
181
|
+
}
|
|
195
182
|
|
|
196
183
|
def write_files() -> None:
|
|
197
184
|
session_dir = self.session_dir(session_id)
|
|
@@ -785,7 +785,6 @@ def bash_tool(ctx: ToolContext, command: str, timeout: int | None = None) -> Too
|
|
|
785
785
|
stderr=subprocess.STDOUT,
|
|
786
786
|
text=True,
|
|
787
787
|
bufsize=1,
|
|
788
|
-
universal_newlines=True,
|
|
789
788
|
start_new_session=os.name == "posix",
|
|
790
789
|
)
|
|
791
790
|
ctx.track_proc(proc)
|
|
@@ -823,10 +822,10 @@ def bash_tool(ctx: ToolContext, command: str, timeout: int | None = None) -> Too
|
|
|
823
822
|
|
|
824
823
|
line = line.rstrip("\n")
|
|
825
824
|
total_line_count += 1
|
|
826
|
-
kept_bytes += len(line.encode("utf-8")) + 1
|
|
827
825
|
|
|
828
826
|
if log_file is None:
|
|
829
827
|
kept_lines.append(line)
|
|
828
|
+
kept_bytes += len(line.encode("utf-8")) + 1
|
|
830
829
|
if kept_bytes > _BASH_MAX_IN_MEMORY_BYTES:
|
|
831
830
|
# Spill to disk. Create the output dir lazily on first
|
|
832
831
|
# spill so runs that stay in memory never touch the FS.
|
|
@@ -864,7 +863,7 @@ def bash_tool(ctx: ToolContext, command: str, timeout: int | None = None) -> Too
|
|
|
864
863
|
|
|
865
864
|
# If truncation happened but we never spilled, save the full output
|
|
866
865
|
# now so the user can inspect it via the "Full output" hint.
|
|
867
|
-
if log_file is None and trunc.
|
|
866
|
+
if log_file is None and trunc.truncated_by:
|
|
868
867
|
try:
|
|
869
868
|
ctx.tool_output_dir.mkdir(parents=True, exist_ok=True)
|
|
870
869
|
log_path.write_text(raw_output, encoding="utf-8")
|
|
@@ -874,7 +873,7 @@ def bash_tool(ctx: ToolContext, command: str, timeout: int | None = None) -> Too
|
|
|
874
873
|
|
|
875
874
|
result = content
|
|
876
875
|
shown_lines = trunc.output_lines
|
|
877
|
-
was_truncated = log_file is not None or trunc.
|
|
876
|
+
was_truncated = log_file is not None or trunc.truncated_by is not None
|
|
878
877
|
if was_truncated:
|
|
879
878
|
if trunc.truncated_by == "bytes":
|
|
880
879
|
if total_line_count <= 1:
|
|
@@ -980,10 +979,9 @@ def _atomic_write_text(path: Path, content: str, *, newline: str | None = None)
|
|
|
980
979
|
|
|
981
980
|
@dataclass(frozen=True)
|
|
982
981
|
class Truncation:
|
|
983
|
-
|
|
982
|
+
# "lines" or "bytes"; None when nothing was cut.
|
|
984
983
|
truncated_by: str | None
|
|
985
984
|
output_lines: int
|
|
986
|
-
output_bytes: int
|
|
987
985
|
|
|
988
986
|
|
|
989
987
|
def truncate_text(
|
|
@@ -1020,26 +1018,14 @@ def truncate_text(
|
|
|
1020
1018
|
encoded = target.encode("utf-8")
|
|
1021
1019
|
sliced = encoded[-max_bytes:] if tail else encoded[:max_bytes]
|
|
1022
1020
|
content = sliced.decode("utf-8", errors="ignore")
|
|
1023
|
-
return content, Truncation(
|
|
1024
|
-
truncated=True,
|
|
1025
|
-
truncated_by="bytes",
|
|
1026
|
-
output_lines=1,
|
|
1027
|
-
output_bytes=len(sliced),
|
|
1028
|
-
)
|
|
1021
|
+
return content, Truncation(truncated_by="bytes", output_lines=1)
|
|
1029
1022
|
|
|
1030
1023
|
content = "\n".join(out_lines)
|
|
1031
|
-
truncated = len(out_lines) < len(lines) or out_bytes < total_bytes
|
|
1032
1024
|
|
|
1033
1025
|
truncated_by: str | None = None
|
|
1034
|
-
if
|
|
1035
|
-
if len(out_lines)
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
truncated_by = "bytes"
|
|
1026
|
+
if len(out_lines) < len(lines):
|
|
1027
|
+
truncated_by = "lines" if len(out_lines) == max_lines else "bytes"
|
|
1028
|
+
elif out_bytes < total_bytes:
|
|
1029
|
+
truncated_by = "bytes"
|
|
1039
1030
|
|
|
1040
|
-
return content, Truncation(
|
|
1041
|
-
truncated=truncated,
|
|
1042
|
-
truncated_by=truncated_by,
|
|
1043
|
-
output_lines=len(out_lines),
|
|
1044
|
-
output_bytes=out_bytes,
|
|
1045
|
-
)
|
|
1031
|
+
return content, Truncation(truncated_by=truncated_by, output_lines=len(out_lines))
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import json
|
|
6
5
|
from pathlib import Path
|
|
7
6
|
from typing import Any
|
|
8
7
|
|
|
@@ -29,18 +28,3 @@ def as_bool(value: Any) -> bool | None:
|
|
|
29
28
|
def omit_none(d: dict[str, Any]) -> dict[str, Any]:
|
|
30
29
|
"""Return a shallow copy of d with None values removed."""
|
|
31
30
|
return {k: v for k, v in d.items() if v is not None}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
def parse_tool_arguments(raw: str | None) -> dict[str, Any] | None:
|
|
35
|
-
"""Parse a JSON tool-arguments string.
|
|
36
|
-
|
|
37
|
-
Returns the parsed dict, or None when the input is missing/invalid/non-object.
|
|
38
|
-
Empty / None input is treated as an empty argument set.
|
|
39
|
-
"""
|
|
40
|
-
if not raw or not raw.strip():
|
|
41
|
-
return {}
|
|
42
|
-
try:
|
|
43
|
-
parsed = json.loads(raw)
|
|
44
|
-
except json.JSONDecodeError:
|
|
45
|
-
return None
|
|
46
|
-
return parsed if isinstance(parsed, dict) else None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|