launchdarkly-ai-opencode-agents 0.0.0__tar.gz → 0.2.0__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.
- launchdarkly_ai_opencode_agents-0.2.0/.gitignore +38 -0
- launchdarkly_ai_opencode_agents-0.2.0/CHANGELOG.md +31 -0
- launchdarkly_ai_opencode_agents-0.2.0/PKG-INFO +61 -0
- launchdarkly_ai_opencode_agents-0.2.0/README.md +39 -0
- launchdarkly_ai_opencode_agents-0.2.0/agents.md +47 -0
- launchdarkly_ai_opencode_agents-0.2.0/api-report/launchdarkly_ai_opencode_agents.txt +4 -0
- launchdarkly_ai_opencode_agents-0.2.0/pyproject.toml +34 -0
- launchdarkly_ai_opencode_agents-0.2.0/src/launchdarkly_ai_opencode_agents/__init__.py +17 -0
- launchdarkly_ai_opencode_agents-0.2.0/src/launchdarkly_ai_opencode_agents/_version.py +4 -0
- launchdarkly_ai_opencode_agents-0.2.0/src/launchdarkly_ai_opencode_agents/handler.py +833 -0
- launchdarkly_ai_opencode_agents-0.2.0/src/launchdarkly_ai_opencode_agents/process.py +254 -0
- launchdarkly_ai_opencode_agents-0.2.0/src/launchdarkly_ai_opencode_agents/py.typed +0 -0
- launchdarkly_ai_opencode_agents-0.2.0/src/launchdarkly_ai_opencode_agents/server.py +249 -0
- launchdarkly_ai_opencode_agents-0.2.0/src/launchdarkly_ai_opencode_agents/spans.py +146 -0
- launchdarkly_ai_opencode_agents-0.2.0/tests/fake_opencode.py +292 -0
- launchdarkly_ai_opencode_agents-0.2.0/tests/test_handler.py +1720 -0
- launchdarkly_ai_opencode_agents-0.2.0/tests/test_sdk_usage.py +87 -0
- launchdarkly_ai_opencode_agents-0.0.0/PKG-INFO +0 -6
- launchdarkly_ai_opencode_agents-0.0.0/pyproject.toml +0 -10
- launchdarkly_ai_opencode_agents-0.0.0/setup.cfg +0 -4
- launchdarkly_ai_opencode_agents-0.0.0/src/launchdarkly_ai_opencode_agents.egg-info/PKG-INFO +0 -6
- launchdarkly_ai_opencode_agents-0.0.0/src/launchdarkly_ai_opencode_agents.egg-info/SOURCES.txt +0 -6
- launchdarkly_ai_opencode_agents-0.0.0/src/launchdarkly_ai_opencode_agents.egg-info/dependency_links.txt +0 -1
- launchdarkly_ai_opencode_agents-0.0.0/src/launchdarkly_ai_opencode_agents.egg-info/top_level.txt +0 -1
- launchdarkly_ai_opencode_agents-0.0.0/src/placeholder_sdk/__init__.py +0 -1
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.pyc
|
|
3
|
+
*.pyo
|
|
4
|
+
.venv/
|
|
5
|
+
dist/
|
|
6
|
+
*.egg-info/
|
|
7
|
+
|
|
8
|
+
# Environment
|
|
9
|
+
.env
|
|
10
|
+
.env.local
|
|
11
|
+
.env.*.local
|
|
12
|
+
|
|
13
|
+
# macOS
|
|
14
|
+
.DS_Store
|
|
15
|
+
|
|
16
|
+
# Editor
|
|
17
|
+
.vscode/
|
|
18
|
+
.cursor/
|
|
19
|
+
|
|
20
|
+
# Logs
|
|
21
|
+
*.log
|
|
22
|
+
|
|
23
|
+
# Test coverage
|
|
24
|
+
coverage/
|
|
25
|
+
.coverage
|
|
26
|
+
htmlcov/
|
|
27
|
+
|
|
28
|
+
# Type checking
|
|
29
|
+
.mypy_cache/
|
|
30
|
+
|
|
31
|
+
# Test cache
|
|
32
|
+
.pytest_cache/
|
|
33
|
+
|
|
34
|
+
# Run output / scratch
|
|
35
|
+
tmp/
|
|
36
|
+
output/
|
|
37
|
+
|
|
38
|
+
.claude/
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.2.0](https://github.com/launchdarkly/python-ai-sdk/compare/launchdarkly-ai-opencode-agents-0.1.0...launchdarkly-ai-opencode-agents-0.2.0) (2026-10-23)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### ⚠ BREAKING CHANGES
|
|
7
|
+
|
|
8
|
+
* **opencode:** start and stop opencode serve in the handler ([#198](https://github.com/launchdarkly/python-ai-sdk/issues/198))
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
* **opencode-agents:** add an OpenCode handler ([#159](https://github.com/launchdarkly/python-ai-sdk/issues/159)) ([fc08e8c](https://github.com/launchdarkly/python-ai-sdk/commit/fc08e8c383bfb6e5e505b7a07d57f5bd9dd79a11))
|
|
13
|
+
* **opencode-agents:** emit the AG-UI text and tool subset ([#162](https://github.com/launchdarkly/python-ai-sdk/issues/162)) ([7677621](https://github.com/launchdarkly/python-ai-sdk/commit/7677621a88ba433a2117b1b5a007631cbf7d74d0))
|
|
14
|
+
* **opencode-agents:** open one OpenCode session per call ([#179](https://github.com/launchdarkly/python-ai-sdk/issues/179)) ([b225113](https://github.com/launchdarkly/python-ai-sdk/commit/b225113d66bbc980cb72c3007620bca061ab6035))
|
|
15
|
+
* **opencode-agents:** render agent instruction templates ([#182](https://github.com/launchdarkly/python-ai-sdk/issues/182)) ([83175aa](https://github.com/launchdarkly/python-ai-sdk/commit/83175aa680dc18697ddcc1d17863cdfdaf08d7e6))
|
|
16
|
+
* **opencode:** start and stop opencode serve in the handler ([#198](https://github.com/launchdarkly/python-ai-sdk/issues/198)) ([0748dcf](https://github.com/launchdarkly/python-ai-sdk/commit/0748dcf209076f98cffa38fc59d1987293d5015f))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### Bug Fixes
|
|
20
|
+
|
|
21
|
+
* **opencode-agents:** own the HTTP client ([#194](https://github.com/launchdarkly/python-ai-sdk/issues/194)) ([ed2d419](https://github.com/launchdarkly/python-ai-sdk/commit/ed2d41914ee3f843fe087d754914cd4f2ae1dac8))
|
|
22
|
+
* **opencode-agents:** skip judges and record structured history ([#177](https://github.com/launchdarkly/python-ai-sdk/issues/177)) ([6fadf1d](https://github.com/launchdarkly/python-ai-sdk/commit/6fadf1da3abc49c0a82ffc4c2532484aceac8d76))
|
|
23
|
+
|
|
24
|
+
## Changelog
|
|
25
|
+
|
|
26
|
+
## Changelog
|
|
27
|
+
|
|
28
|
+
All notable changes to this project will be documented in this file.
|
|
29
|
+
|
|
30
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
31
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: launchdarkly-ai-opencode-agents
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: OpenCode agent handler for LaunchDarkly AI SDK
|
|
5
|
+
Project-URL: Homepage, https://github.com/launchdarkly/python-ai-sdk
|
|
6
|
+
Project-URL: Repository, https://github.com/launchdarkly/python-ai-sdk
|
|
7
|
+
Project-URL: Bug Tracker, https://github.com/launchdarkly/python-ai-sdk/issues
|
|
8
|
+
Author-email: LaunchDarkly <team@launchdarkly.com>
|
|
9
|
+
License-Expression: Apache-2.0
|
|
10
|
+
Keywords: agents,ai,launchdarkly,opencode
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
17
|
+
Requires-Python: >=3.12
|
|
18
|
+
Requires-Dist: httpx>=0.27
|
|
19
|
+
Requires-Dist: launchdarkly-ai-server
|
|
20
|
+
Requires-Dist: opentelemetry-api>=1.25
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# `launchdarkly-ai-opencode-agents`
|
|
24
|
+
|
|
25
|
+
OpenCode handler for `launchdarkly-ai-server`. `async with handler` starts `opencode serve` and waits until it is healthy. The process stops when that context exits. It does not depend on an OpenCode SDK.
|
|
26
|
+
|
|
27
|
+
**`provides_for`:** `['*', 'agent']` — matches an agent-mode variation for any provider.
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install launchdarkly-ai-server launchdarkly-server-sdk launchdarkly-ai-opencode-agents
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
import asyncio
|
|
35
|
+
import os
|
|
36
|
+
|
|
37
|
+
from launchdarkly_ai_opencode_agents import create_opencode_handler
|
|
38
|
+
from launchdarkly_ai_server import config, init_client, shutdown
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
async def main() -> None:
|
|
42
|
+
await init_client()
|
|
43
|
+
handler = create_opencode_handler(
|
|
44
|
+
cwd="/work",
|
|
45
|
+
config="/home/app/.vega/opencode.json",
|
|
46
|
+
env=os.environ.copy(),
|
|
47
|
+
)
|
|
48
|
+
async with handler:
|
|
49
|
+
async for event in config(key="my-agent", handler=handler).stream(
|
|
50
|
+
"Hello", {"kind": "user", "key": "user-1"}
|
|
51
|
+
):
|
|
52
|
+
print(event)
|
|
53
|
+
await shutdown()
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
asyncio.run(main())
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`cwd` is the working directory, `config` is the OpenCode config file, and `env` is the child environment. The handler sets `OPENCODE_CONFIG` from `config`, replaces any `OPENCODE_SERVER_USERNAME` in `env` with `opencode`, replaces any `OPENCODE_SERVER_PASSWORD` with a generated secret, and binds `opencode serve` to loopback. Requests to that server use that username and secret as basic auth. A caller does not pass a URL, an HTTP client, or a transport. `command` overrides the `opencode` executable; a test passes a fake executable there.
|
|
60
|
+
|
|
61
|
+
Entering the handler starts `opencode serve` and returns once it is healthy. The process stops when the context exits, after `config().stream()` returns. Put a wall-clock limit around `config().stream()`, not around the context. A judge routed here runs as a full OpenCode agent: the server's tools, in `cwd`, apply to the judge prompt. There is no tool-less judge mode. The handler yields AG-UI text and tool events. `config().stream()` yields `chunk` then `done`.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# `launchdarkly-ai-opencode-agents`
|
|
2
|
+
|
|
3
|
+
OpenCode handler for `launchdarkly-ai-server`. `async with handler` starts `opencode serve` and waits until it is healthy. The process stops when that context exits. It does not depend on an OpenCode SDK.
|
|
4
|
+
|
|
5
|
+
**`provides_for`:** `['*', 'agent']` — matches an agent-mode variation for any provider.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install launchdarkly-ai-server launchdarkly-server-sdk launchdarkly-ai-opencode-agents
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```python
|
|
12
|
+
import asyncio
|
|
13
|
+
import os
|
|
14
|
+
|
|
15
|
+
from launchdarkly_ai_opencode_agents import create_opencode_handler
|
|
16
|
+
from launchdarkly_ai_server import config, init_client, shutdown
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
async def main() -> None:
|
|
20
|
+
await init_client()
|
|
21
|
+
handler = create_opencode_handler(
|
|
22
|
+
cwd="/work",
|
|
23
|
+
config="/home/app/.vega/opencode.json",
|
|
24
|
+
env=os.environ.copy(),
|
|
25
|
+
)
|
|
26
|
+
async with handler:
|
|
27
|
+
async for event in config(key="my-agent", handler=handler).stream(
|
|
28
|
+
"Hello", {"kind": "user", "key": "user-1"}
|
|
29
|
+
):
|
|
30
|
+
print(event)
|
|
31
|
+
await shutdown()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
asyncio.run(main())
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`cwd` is the working directory, `config` is the OpenCode config file, and `env` is the child environment. The handler sets `OPENCODE_CONFIG` from `config`, replaces any `OPENCODE_SERVER_USERNAME` in `env` with `opencode`, replaces any `OPENCODE_SERVER_PASSWORD` with a generated secret, and binds `opencode serve` to loopback. Requests to that server use that username and secret as basic auth. A caller does not pass a URL, an HTTP client, or a transport. `command` overrides the `opencode` executable; a test passes a fake executable there.
|
|
38
|
+
|
|
39
|
+
Entering the handler starts `opencode serve` and returns once it is healthy. The process stops when the context exits, after `config().stream()` returns. Put a wall-clock limit around `config().stream()`, not around the context. A judge routed here runs as a full OpenCode agent: the server's tools, in `cwd`, apply to the judge prompt. There is no tool-less judge mode. The handler yields AG-UI text and tool events. `config().stream()` yields `chunk` then `done`.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Agent Guide — `launchdarkly-ai-opencode-agents`
|
|
2
|
+
|
|
3
|
+
```python
|
|
4
|
+
provides_for = ('*', 'agent')
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
Each call starts `opencode serve`, speaks the stable HTTP API (`/session`, `/session/{id}/message`, `/event`, `/session/{id}/abort`), and stops the process when the stream finishes or the caller closes it. The package does not import or construct `anomalyco/opencode-sdk-python`.
|
|
8
|
+
|
|
9
|
+
This package is Python-only. There is no JS counterpart in this slice. The spec section, API surface list, and `usage-ids.txt` row live in `launchdarkly/ai-sdks-monorepo`, which is not this repository. The usage helper ids are `opencode-agents.createOpenCodeHandler` and `opencode-agents.opencodeAgents`. The package is a normal pre-v1 package, same as the other handlers.
|
|
10
|
+
|
|
11
|
+
## File map
|
|
12
|
+
|
|
13
|
+
| File | Role |
|
|
14
|
+
| --- | --- |
|
|
15
|
+
| `handler.py` | Reducer, `create_opencode_handler`, `opencode_agents` |
|
|
16
|
+
| `process.py` | Starts and stops `opencode serve` for one call |
|
|
17
|
+
| `server.py` | The HTTP session this process just started |
|
|
18
|
+
| `spans.py` | `invoke_agent` root, and the `chat` / `execute_tool` constructors the parity checks call |
|
|
19
|
+
| `_version.py` | Package name and version sent with usage events |
|
|
20
|
+
|
|
21
|
+
## Dependencies
|
|
22
|
+
|
|
23
|
+
`launchdarkly-ai-server`, `httpx`, and `opentelemetry-api`. `launchdarkly-server-sdk` is what `init_client()` loads for a normal process. No OpenCode SDK. The `opencode` executable is the default command.
|
|
24
|
+
|
|
25
|
+
## init_client
|
|
26
|
+
|
|
27
|
+
Call `init_client()` before `config()`, the same way as the other handlers. The example in `README.md` does.
|
|
28
|
+
|
|
29
|
+
## OTel
|
|
30
|
+
|
|
31
|
+
A run opens one `invoke_agent` span and sets `launchdarkly.operation.type`. The handler stream is the AG-UI text and tool subset. `config().stream()` is `chunk` then `done`.
|
|
32
|
+
|
|
33
|
+
## Call shape
|
|
34
|
+
|
|
35
|
+
`create_opencode_handler(*, cwd, config, env, capture_content=None, model=None, agent=None, command=None)` returns a `ProviderHandler`. `cwd` is the working directory. `config` is the path of the OpenCode config file. `env` replaces the child environment, and the handler sets `OPENCODE_CONFIG` from `config`. Each start also sets `OPENCODE_SERVER_USERNAME` to `opencode` and `OPENCODE_SERVER_PASSWORD` to a generated secret, replacing either value inherited from `env`, and sends that pair as basic auth. `command` defaults to `opencode`. A test passes a fake executable instead of a URL. There is no `base_url`, client, or transport argument.
|
|
36
|
+
|
|
37
|
+
`async with handler` starts the process and returns once `/global/health` returns 200 and `healthy: true`. Concurrent entries share that process. The process stops when the last context exits, after `config().stream()` returns, including when the caller times out or closes the stream. A wall-clock budget goes around `config().stream()`, not around the context. Closing the stream still aborts the session. Without the context, one call starts and stops its own process.
|
|
38
|
+
|
|
39
|
+
Assistant text is emitted only after `message.updated` marks that message `role == "assistant"`. A part snapshot is the full text of that part so far; a later delta appends. A text delta without `partID` is dropped.
|
|
40
|
+
|
|
41
|
+
## Common pitfalls
|
|
42
|
+
|
|
43
|
+
`provides_for` is `('*', 'agent')`, so a judge is routed here too. Each call starts its own process, so the judge prompt does not enter the agent's session and does not need `skip_judges=True`. The judge still runs as a full OpenCode agent, with the server's tools, in `cwd`. This package does not take a tool-less judge agent.
|
|
44
|
+
|
|
45
|
+
The reducer does not adopt the first session on the bus. Other sessions are dropped, including their `session.error`. `session.idle` for this session ends the run.
|
|
46
|
+
|
|
47
|
+
`env` is not merged with the parent environment. A bare `opencode` command needs `PATH` in `env`.
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
# Public API of launchdarkly_ai_opencode_agents. Generated by `make api-report`; do not edit.
|
|
2
|
+
create_opencode_handler(*, cwd: str | os.PathLike[str], config: str | os.PathLike[str], env: Mapping[str, str], capture_content: bool | None = None, model: ModelResolver | None = None, agent: str | None = None, command: Command = None) -> ProviderHandler
|
|
3
|
+
opencode_agents(config_key: str, user_input: str, context: dict[str, Any], **kwargs: Any) -> Any
|
|
4
|
+
OpenCodeSessionError(Exception) [class]
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "launchdarkly-ai-opencode-agents"
|
|
3
|
+
version = "0.2.0"
|
|
4
|
+
requires-python = ">=3.12"
|
|
5
|
+
dependencies = [
|
|
6
|
+
"launchdarkly-ai-server",
|
|
7
|
+
"httpx>=0.27",
|
|
8
|
+
"opentelemetry-api>=1.25",
|
|
9
|
+
]
|
|
10
|
+
description = "OpenCode agent handler for LaunchDarkly AI SDK"
|
|
11
|
+
readme = "README.md"
|
|
12
|
+
license = "Apache-2.0"
|
|
13
|
+
authors = [{name = "LaunchDarkly", email = "team@launchdarkly.com"}]
|
|
14
|
+
keywords = ["launchdarkly", "ai", "opencode", "agents"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 3 - Alpha",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"License :: OSI Approved :: Apache Software License",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.12",
|
|
21
|
+
"Topic :: Software Development :: Libraries",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
[project.urls]
|
|
25
|
+
Homepage = "https://github.com/launchdarkly/python-ai-sdk"
|
|
26
|
+
Repository = "https://github.com/launchdarkly/python-ai-sdk"
|
|
27
|
+
"Bug Tracker" = "https://github.com/launchdarkly/python-ai-sdk/issues"
|
|
28
|
+
|
|
29
|
+
[build-system]
|
|
30
|
+
requires = ["hatchling>=1.32,<2"]
|
|
31
|
+
build-backend = "hatchling.build"
|
|
32
|
+
|
|
33
|
+
[tool.hatch.build.targets.wheel]
|
|
34
|
+
packages = ["src/launchdarkly_ai_opencode_agents"]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""LaunchDarkly AI SDK - integration for OpenCode agents.
|
|
2
|
+
|
|
3
|
+
See https://launchdarkly.com/docs for usage.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from launchdarkly_ai_server import register_ai_sdk_package
|
|
7
|
+
|
|
8
|
+
from ._version import PACKAGE_NAME, __version__
|
|
9
|
+
from .handler import OpenCodeSessionError, create_opencode_handler, opencode_agents
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"OpenCodeSessionError",
|
|
13
|
+
"create_opencode_handler",
|
|
14
|
+
"opencode_agents",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
register_ai_sdk_package(PACKAGE_NAME, __version__)
|