opencode-agent-sdk 0.4.6__tar.gz → 0.4.8__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.
- {opencode_agent_sdk-0.4.6 → opencode_agent_sdk-0.4.8}/PKG-INFO +7 -3
- {opencode_agent_sdk-0.4.6 → opencode_agent_sdk-0.4.8}/README.md +6 -2
- {opencode_agent_sdk-0.4.6 → opencode_agent_sdk-0.4.8}/pyproject.toml +1 -1
- {opencode_agent_sdk-0.4.6 → opencode_agent_sdk-0.4.8}/src/opencode_agent_sdk/__init__.py +1 -1
- opencode_agent_sdk-0.4.8/src/opencode_agent_sdk/_errors.py +29 -0
- {opencode_agent_sdk-0.4.6 → opencode_agent_sdk-0.4.8}/src/opencode_agent_sdk/_internal/acp.py +19 -4
- {opencode_agent_sdk-0.4.6 → opencode_agent_sdk-0.4.8}/src/opencode_agent_sdk/_internal/http_transport.py +9 -5
- {opencode_agent_sdk-0.4.6 → opencode_agent_sdk-0.4.8}/src/opencode_agent_sdk/types.py +10 -1
- {opencode_agent_sdk-0.4.6 → opencode_agent_sdk-0.4.8}/src/opencode_agent_sdk.egg-info/PKG-INFO +7 -3
- {opencode_agent_sdk-0.4.6 → opencode_agent_sdk-0.4.8}/src/opencode_agent_sdk.egg-info/SOURCES.txt +2 -1
- opencode_agent_sdk-0.4.8/tests/test_types.py +22 -0
- opencode_agent_sdk-0.4.6/src/opencode_agent_sdk/_errors.py +0 -9
- {opencode_agent_sdk-0.4.6 → opencode_agent_sdk-0.4.8}/setup.cfg +0 -0
- {opencode_agent_sdk-0.4.6 → opencode_agent_sdk-0.4.8}/src/opencode_agent_sdk/_internal/__init__.py +0 -0
- {opencode_agent_sdk-0.4.6 → opencode_agent_sdk-0.4.8}/src/opencode_agent_sdk/_internal/transport.py +0 -0
- {opencode_agent_sdk-0.4.6 → opencode_agent_sdk-0.4.8}/src/opencode_agent_sdk/_mcp_bridge.py +0 -0
- {opencode_agent_sdk-0.4.6 → opencode_agent_sdk-0.4.8}/src/opencode_agent_sdk/client.py +0 -0
- {opencode_agent_sdk-0.4.6 → opencode_agent_sdk-0.4.8}/src/opencode_agent_sdk/model_registry.py +0 -0
- {opencode_agent_sdk-0.4.6 → opencode_agent_sdk-0.4.8}/src/opencode_agent_sdk/tools.py +0 -0
- {opencode_agent_sdk-0.4.6 → opencode_agent_sdk-0.4.8}/src/opencode_agent_sdk.egg-info/dependency_links.txt +0 -0
- {opencode_agent_sdk-0.4.6 → opencode_agent_sdk-0.4.8}/src/opencode_agent_sdk.egg-info/requires.txt +0 -0
- {opencode_agent_sdk-0.4.6 → opencode_agent_sdk-0.4.8}/src/opencode_agent_sdk.egg-info/top_level.txt +0 -0
- {opencode_agent_sdk-0.4.6 → opencode_agent_sdk-0.4.8}/tests/test_common.py +0 -0
- {opencode_agent_sdk-0.4.6 → opencode_agent_sdk-0.4.8}/tests/test_model_registry.py +0 -0
- {opencode_agent_sdk-0.4.6 → opencode_agent_sdk-0.4.8}/tests/test_opencode_agent.py +0 -0
- {opencode_agent_sdk-0.4.6 → opencode_agent_sdk-0.4.8}/tests/test_runner.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opencode-agent-sdk
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.8
|
|
4
4
|
Summary: Open-source Agent SDK backed by OpenCode ACP (drop-in replacement for claude_agent_sdk)
|
|
5
5
|
Author: OpenCode
|
|
6
6
|
License: MIT
|
|
@@ -71,9 +71,13 @@ async def main():
|
|
|
71
71
|
asyncio.run(main())
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
-
##
|
|
74
|
+
## Why OpenCode Agent SDK?
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
- **Drop-in Replacement**: Seamlessly switch from `claude_agent_sdk` by just changing your imports.
|
|
77
|
+
- **Open Source & Headless**: Take full control of your agent infrastructure. No more proprietary black boxes.
|
|
78
|
+
- **Multi-Provider Support**: Use any LLM (Anthropic, OpenAI, xAI, Google, Locall) via OpenCode's backend.
|
|
79
|
+
- **Native SSE Streaming**: Real-time response streaming for a better user experience.
|
|
80
|
+
- **Advanced Control**: Fine-grained tool permission hooks and MCP server support builtin.
|
|
77
81
|
|
|
78
82
|
- **HTTP mode** — communicates with a running `opencode serve` instance over REST
|
|
79
83
|
- **Subprocess mode** — spawns `opencode acp` locally over stdio JSON-RPC
|
|
@@ -47,9 +47,13 @@ async def main():
|
|
|
47
47
|
asyncio.run(main())
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
##
|
|
50
|
+
## Why OpenCode Agent SDK?
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
- **Drop-in Replacement**: Seamlessly switch from `claude_agent_sdk` by just changing your imports.
|
|
53
|
+
- **Open Source & Headless**: Take full control of your agent infrastructure. No more proprietary black boxes.
|
|
54
|
+
- **Multi-Provider Support**: Use any LLM (Anthropic, OpenAI, xAI, Google, Locall) via OpenCode's backend.
|
|
55
|
+
- **Native SSE Streaming**: Real-time response streaming for a better user experience.
|
|
56
|
+
- **Advanced Control**: Fine-grained tool permission hooks and MCP server support builtin.
|
|
53
57
|
|
|
54
58
|
- **HTTP mode** — communicates with a running `opencode serve` instance over REST
|
|
55
59
|
- **Subprocess mode** — spawns `opencode acp` locally over stdio JSON-RPC
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "opencode-agent-sdk"
|
|
7
|
-
version = "0.4.
|
|
7
|
+
version = "0.4.8"
|
|
8
8
|
description = "Open-source Agent SDK backed by OpenCode ACP (drop-in replacement for claude_agent_sdk)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class SDKError(Exception):
|
|
5
|
+
"""Base exception for all OpenCode Agent SDK errors."""
|
|
6
|
+
pass
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ProcessError(SDKError):
|
|
10
|
+
"""Raised when the opencode subprocess exits with an error or fails to start."""
|
|
11
|
+
def __init__(self, message: str, exit_code: int | None = None) -> None:
|
|
12
|
+
super().__init__(message)
|
|
13
|
+
self.exit_code = exit_code
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class TransportError(SDKError):
|
|
17
|
+
"""Raised when there is a communication error with the OpenCode server."""
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class SessionError(SDKError):
|
|
22
|
+
"""Raised when a session-related error occurs (e.g., missing session, expiration)."""
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ToolError(SDKError):
|
|
27
|
+
"""Raised when a tool execution hook fails or returns an error."""
|
|
28
|
+
pass
|
|
29
|
+
|
{opencode_agent_sdk-0.4.6 → opencode_agent_sdk-0.4.8}/src/opencode_agent_sdk/_internal/acp.py
RENAMED
|
@@ -341,15 +341,28 @@ class ACPSession:
|
|
|
341
341
|
text = content.get("text", "")
|
|
342
342
|
if text:
|
|
343
343
|
self._text_buffer += text
|
|
344
|
+
# Flush immediately so consumers can stream text incrementally
|
|
345
|
+
yield AssistantMessage(
|
|
346
|
+
content=[TextBlock(text=self._text_buffer)]
|
|
347
|
+
)
|
|
348
|
+
self._text_buffer = ""
|
|
344
349
|
|
|
345
350
|
elif update_type == "tool_call":
|
|
346
351
|
tool_call_id = session_update.get("toolCallId", "")
|
|
352
|
+
tool_name = session_update.get("title", "")
|
|
347
353
|
self._tool_calls[tool_call_id] = {
|
|
348
354
|
"id": tool_call_id,
|
|
349
|
-
"name":
|
|
355
|
+
"name": tool_name,
|
|
350
356
|
"input": session_update.get("rawInput", {}),
|
|
351
357
|
"status": session_update.get("status", "pending"),
|
|
352
358
|
}
|
|
359
|
+
# Flush text buffer before tool starts so consumers see
|
|
360
|
+
# accumulated text immediately rather than after tool completes
|
|
361
|
+
if self._text_buffer:
|
|
362
|
+
yield AssistantMessage(
|
|
363
|
+
content=[TextBlock(text=self._text_buffer)]
|
|
364
|
+
)
|
|
365
|
+
self._text_buffer = ""
|
|
353
366
|
|
|
354
367
|
elif update_type == "tool_call_update":
|
|
355
368
|
tool_call_id = session_update.get("toolCallId", "")
|
|
@@ -396,10 +409,12 @@ class ACPSession:
|
|
|
396
409
|
)
|
|
397
410
|
|
|
398
411
|
elif update_type == "agent_thought_chunk":
|
|
412
|
+
# Yield thinking text as AssistantMessage so consumers can
|
|
413
|
+
# stream it without needing to handle SystemMessage separately.
|
|
414
|
+
# Kept separate from _text_buffer to avoid mixing with response text.
|
|
399
415
|
content = session_update.get("content", {})
|
|
400
416
|
text = content.get("text", "")
|
|
401
417
|
if text:
|
|
402
|
-
yield
|
|
403
|
-
|
|
404
|
-
data={"text": text},
|
|
418
|
+
yield AssistantMessage(
|
|
419
|
+
content=[TextBlock(text=text)]
|
|
405
420
|
)
|
|
@@ -254,7 +254,7 @@ class HTTPTransport:
|
|
|
254
254
|
data={
|
|
255
255
|
"tool_name": tool_name,
|
|
256
256
|
"tool_id": part.get("callID", part_id),
|
|
257
|
-
"error": str(state),
|
|
257
|
+
"error": str(state.get("error", state)),
|
|
258
258
|
},
|
|
259
259
|
)
|
|
260
260
|
|
|
@@ -263,11 +263,15 @@ class HTTPTransport:
|
|
|
263
263
|
|
|
264
264
|
elif part_type == "step-finish":
|
|
265
265
|
tokens = part.get("tokens", {})
|
|
266
|
+
from ..types import Usage
|
|
267
|
+
usage = Usage(
|
|
268
|
+
input_tokens=int(tokens.get("input", 0)),
|
|
269
|
+
output_tokens=int(tokens.get("output", 0)),
|
|
270
|
+
cache_creation_input_tokens=tokens.get("cache_creation_input_tokens"),
|
|
271
|
+
cache_read_input_tokens=tokens.get("cache_read_input_tokens"),
|
|
272
|
+
)
|
|
266
273
|
return ResultMessage(
|
|
267
|
-
usage=
|
|
268
|
-
"input_tokens": int(tokens.get("input", 0)),
|
|
269
|
-
"output_tokens": int(tokens.get("output", 0)),
|
|
270
|
-
},
|
|
274
|
+
usage=usage,
|
|
271
275
|
total_cost_usd=part.get("cost", 0.0),
|
|
272
276
|
session_id=part.get("sessionID", self._session_id),
|
|
273
277
|
duration_ms=0.0,
|
|
@@ -4,6 +4,15 @@ from dataclasses import dataclass, field
|
|
|
4
4
|
from typing import Any, Callable
|
|
5
5
|
|
|
6
6
|
|
|
7
|
+
@dataclass
|
|
8
|
+
class Usage:
|
|
9
|
+
"""Usage stats for a response."""
|
|
10
|
+
input_tokens: int = 0
|
|
11
|
+
output_tokens: int = 0
|
|
12
|
+
cache_creation_input_tokens: int | None = None
|
|
13
|
+
cache_read_input_tokens: int | None = None
|
|
14
|
+
|
|
15
|
+
|
|
7
16
|
@dataclass
|
|
8
17
|
class TextBlock:
|
|
9
18
|
text: str
|
|
@@ -26,7 +35,7 @@ class AssistantMessage:
|
|
|
26
35
|
|
|
27
36
|
@dataclass
|
|
28
37
|
class ResultMessage:
|
|
29
|
-
usage:
|
|
38
|
+
usage: Usage = field(default_factory=Usage)
|
|
30
39
|
total_cost_usd: float = 0.0
|
|
31
40
|
session_id: str = ""
|
|
32
41
|
duration_ms: float = 0.0
|
{opencode_agent_sdk-0.4.6 → opencode_agent_sdk-0.4.8}/src/opencode_agent_sdk.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opencode-agent-sdk
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.8
|
|
4
4
|
Summary: Open-source Agent SDK backed by OpenCode ACP (drop-in replacement for claude_agent_sdk)
|
|
5
5
|
Author: OpenCode
|
|
6
6
|
License: MIT
|
|
@@ -71,9 +71,13 @@ async def main():
|
|
|
71
71
|
asyncio.run(main())
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
-
##
|
|
74
|
+
## Why OpenCode Agent SDK?
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
- **Drop-in Replacement**: Seamlessly switch from `claude_agent_sdk` by just changing your imports.
|
|
77
|
+
- **Open Source & Headless**: Take full control of your agent infrastructure. No more proprietary black boxes.
|
|
78
|
+
- **Multi-Provider Support**: Use any LLM (Anthropic, OpenAI, xAI, Google, Locall) via OpenCode's backend.
|
|
79
|
+
- **Native SSE Streaming**: Real-time response streaming for a better user experience.
|
|
80
|
+
- **Advanced Control**: Fine-grained tool permission hooks and MCP server support builtin.
|
|
77
81
|
|
|
78
82
|
- **HTTP mode** — communicates with a running `opencode serve` instance over REST
|
|
79
83
|
- **Subprocess mode** — spawns `opencode acp` locally over stdio JSON-RPC
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import unittest
|
|
3
|
+
from opencode_agent_sdk.types import ResultMessage, Usage
|
|
4
|
+
|
|
5
|
+
class TestTypes(unittest.TestCase):
|
|
6
|
+
def test_result_message_usage_robustness(self):
|
|
7
|
+
# Testing the new Usage class integration
|
|
8
|
+
usage = Usage(input_tokens=10, output_tokens=20)
|
|
9
|
+
msg = ResultMessage(usage=usage, total_cost_usd=0.01)
|
|
10
|
+
|
|
11
|
+
self.assertEqual(msg.usage.input_tokens, 10)
|
|
12
|
+
self.assertEqual(msg.usage.output_tokens, 20)
|
|
13
|
+
self.assertEqual(msg.total_cost_usd, 0.01)
|
|
14
|
+
|
|
15
|
+
def test_usage_default_values(self):
|
|
16
|
+
usage = Usage()
|
|
17
|
+
self.assertEqual(usage.input_tokens, 0)
|
|
18
|
+
self.assertEqual(usage.output_tokens, 0)
|
|
19
|
+
self.assertIsNone(usage.cache_read_input_tokens)
|
|
20
|
+
|
|
21
|
+
if __name__ == "__main__":
|
|
22
|
+
unittest.main()
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class ProcessError(Exception):
|
|
5
|
-
"""Raised when the opencode subprocess exits with an error."""
|
|
6
|
-
|
|
7
|
-
def __init__(self, message: str, exit_code: int | None = None) -> None:
|
|
8
|
-
super().__init__(message)
|
|
9
|
-
self.exit_code = exit_code
|
|
File without changes
|
{opencode_agent_sdk-0.4.6 → opencode_agent_sdk-0.4.8}/src/opencode_agent_sdk/_internal/__init__.py
RENAMED
|
File without changes
|
{opencode_agent_sdk-0.4.6 → opencode_agent_sdk-0.4.8}/src/opencode_agent_sdk/_internal/transport.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{opencode_agent_sdk-0.4.6 → opencode_agent_sdk-0.4.8}/src/opencode_agent_sdk/model_registry.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{opencode_agent_sdk-0.4.6 → opencode_agent_sdk-0.4.8}/src/opencode_agent_sdk.egg-info/requires.txt
RENAMED
|
File without changes
|
{opencode_agent_sdk-0.4.6 → opencode_agent_sdk-0.4.8}/src/opencode_agent_sdk.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|