ocode 0.1.0__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.
- ocode/__init__.py +26 -0
- ocode/agent.py +137 -0
- ocode/cli.py +208 -0
- ocode/client.py +331 -0
- ocode/config.py +108 -0
- ocode/exceptions.py +25 -0
- ocode/logging.py +16 -0
- ocode/managers.py +147 -0
- ocode/models.py +141 -0
- ocode/orchestrator.py +293 -0
- ocode-0.1.0.dist-info/METADATA +209 -0
- ocode-0.1.0.dist-info/RECORD +15 -0
- ocode-0.1.0.dist-info/WHEEL +5 -0
- ocode-0.1.0.dist-info/entry_points.txt +2 -0
- ocode-0.1.0.dist-info/top_level.txt +1 -0
ocode/__init__.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from .agent import Agent
|
|
2
|
+
from .client import OpenCodeClient
|
|
3
|
+
from .config import OpenCodeConfig
|
|
4
|
+
from .exceptions import (
|
|
5
|
+
MCPReadinessError,
|
|
6
|
+
OpenCodeError,
|
|
7
|
+
OpenCodeHTTPError,
|
|
8
|
+
SessionNotFoundError,
|
|
9
|
+
TaskTimeoutError,
|
|
10
|
+
ToolNotFoundError,
|
|
11
|
+
)
|
|
12
|
+
from .orchestrator import MultiAgentRunner, SingleAgentRunner
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"Agent",
|
|
16
|
+
"MCPReadinessError",
|
|
17
|
+
"MultiAgentRunner",
|
|
18
|
+
"OpenCodeClient",
|
|
19
|
+
"OpenCodeConfig",
|
|
20
|
+
"OpenCodeError",
|
|
21
|
+
"OpenCodeHTTPError",
|
|
22
|
+
"SessionNotFoundError",
|
|
23
|
+
"SingleAgentRunner",
|
|
24
|
+
"TaskTimeoutError",
|
|
25
|
+
"ToolNotFoundError",
|
|
26
|
+
]
|
ocode/agent.py
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import asdict, is_dataclass
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from .client import OpenCodeClient
|
|
7
|
+
from .config import OpenCodeConfig
|
|
8
|
+
from .managers import MCPManager, SessionManager, ToolManager
|
|
9
|
+
from .models import ExecutionMetadata, ExecutionResult, StreamEvent
|
|
10
|
+
from .orchestrator import EventStreamer, SingleAgentRunner
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Agent:
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
name: str = "OpenCodeAgent",
|
|
17
|
+
system_prompt: str | None = None,
|
|
18
|
+
*,
|
|
19
|
+
agent: str | None = None,
|
|
20
|
+
model: str | dict[str, Any] | None = None,
|
|
21
|
+
tools: list[str] | None = None,
|
|
22
|
+
mcp_servers: list[str] | None = None,
|
|
23
|
+
project_path: str | None = None,
|
|
24
|
+
base_url: str | None = None,
|
|
25
|
+
streaming: bool | None = None,
|
|
26
|
+
verbose: bool | None = None,
|
|
27
|
+
):
|
|
28
|
+
config = OpenCodeConfig.from_env()
|
|
29
|
+
if project_path is not None:
|
|
30
|
+
config.project_path = project_path
|
|
31
|
+
if base_url is not None:
|
|
32
|
+
config.base_url = base_url
|
|
33
|
+
if streaming is not None:
|
|
34
|
+
config.streaming = streaming
|
|
35
|
+
if verbose is not None:
|
|
36
|
+
config.verbose = verbose
|
|
37
|
+
if model is not None:
|
|
38
|
+
config.default_model = model
|
|
39
|
+
if agent is not None:
|
|
40
|
+
config.default_agent = agent
|
|
41
|
+
if tools is not None:
|
|
42
|
+
config.default_tools = tools
|
|
43
|
+
|
|
44
|
+
self.name = name
|
|
45
|
+
self.system_prompt = system_prompt
|
|
46
|
+
self.default_mcp_servers = mcp_servers or []
|
|
47
|
+
self.config = config
|
|
48
|
+
self.client = OpenCodeClient(config=config)
|
|
49
|
+
self.session_manager = SessionManager(self.client)
|
|
50
|
+
self.tool_manager = ToolManager(self.client)
|
|
51
|
+
self.mcp_manager = MCPManager(self.client)
|
|
52
|
+
self.runner = SingleAgentRunner(
|
|
53
|
+
client=self.client,
|
|
54
|
+
config=self.config,
|
|
55
|
+
session_manager=self.session_manager,
|
|
56
|
+
tool_manager=self.tool_manager,
|
|
57
|
+
mcp_manager=self.mcp_manager,
|
|
58
|
+
)
|
|
59
|
+
self.event_streamer = EventStreamer(self.client)
|
|
60
|
+
self.session_id: str | None = None
|
|
61
|
+
|
|
62
|
+
def close(self) -> None:
|
|
63
|
+
self.client.close()
|
|
64
|
+
|
|
65
|
+
def set_model(self, model: str | dict[str, Any] | None) -> None:
|
|
66
|
+
self.config.default_model = model
|
|
67
|
+
|
|
68
|
+
def list_models(self, include_details: bool = False) -> list[Any]:
|
|
69
|
+
return self.client.list_available_models(include_details=include_details)
|
|
70
|
+
|
|
71
|
+
def run(
|
|
72
|
+
self,
|
|
73
|
+
user_prompt: str,
|
|
74
|
+
*,
|
|
75
|
+
agent: str | None = None,
|
|
76
|
+
model: str | dict[str, Any] | None = None,
|
|
77
|
+
tools: list[str] | None = None,
|
|
78
|
+
mcp_servers: list[str] | None = None,
|
|
79
|
+
session_id: str | None = None,
|
|
80
|
+
return_result: bool = False,
|
|
81
|
+
) -> str | ExecutionResult:
|
|
82
|
+
result = self.runner.run(
|
|
83
|
+
user_prompt,
|
|
84
|
+
agent=agent,
|
|
85
|
+
model=model,
|
|
86
|
+
tools=tools,
|
|
87
|
+
mcp_servers=mcp_servers or self.default_mcp_servers or None,
|
|
88
|
+
session_id=session_id or self.session_id,
|
|
89
|
+
title=self.name,
|
|
90
|
+
system=self.system_prompt,
|
|
91
|
+
)
|
|
92
|
+
self.session_id = result.metadata.session_id
|
|
93
|
+
return result if return_result else result.output
|
|
94
|
+
|
|
95
|
+
def run_async(
|
|
96
|
+
self,
|
|
97
|
+
user_prompt: str,
|
|
98
|
+
*,
|
|
99
|
+
agent: str | None = None,
|
|
100
|
+
model: str | dict[str, Any] | None = None,
|
|
101
|
+
tools: list[str] | None = None,
|
|
102
|
+
session_id: str | None = None,
|
|
103
|
+
) -> ExecutionMetadata:
|
|
104
|
+
metadata = self.runner.run_async(
|
|
105
|
+
user_prompt,
|
|
106
|
+
agent=agent,
|
|
107
|
+
model=model,
|
|
108
|
+
tools=tools,
|
|
109
|
+
session_id=session_id or self.session_id,
|
|
110
|
+
title=self.name,
|
|
111
|
+
)
|
|
112
|
+
self.session_id = metadata.session_id
|
|
113
|
+
return metadata
|
|
114
|
+
|
|
115
|
+
def run_shell(
|
|
116
|
+
self,
|
|
117
|
+
command: str,
|
|
118
|
+
*,
|
|
119
|
+
agent: str | None = None,
|
|
120
|
+
model: str | dict[str, Any] | None = None,
|
|
121
|
+
return_result: bool = False,
|
|
122
|
+
) -> str | ExecutionResult:
|
|
123
|
+
if not self.session_id:
|
|
124
|
+
self.session_id = self.client.create_session(title=self.name).id
|
|
125
|
+
result = self.runner.run_shell(command, session_id=self.session_id, agent=agent, model=model)
|
|
126
|
+
return result if return_result else result.output
|
|
127
|
+
|
|
128
|
+
def stream(self, *, limit: int = 10) -> list[StreamEvent]:
|
|
129
|
+
return self.event_streamer.stream(limit=limit)
|
|
130
|
+
|
|
131
|
+
@staticmethod
|
|
132
|
+
def to_dict(value: Any) -> Any:
|
|
133
|
+
if isinstance(value, list):
|
|
134
|
+
return [Agent.to_dict(item) for item in value]
|
|
135
|
+
if is_dataclass(value):
|
|
136
|
+
return asdict(value)
|
|
137
|
+
return value
|
ocode/cli.py
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
from dataclasses import asdict, is_dataclass
|
|
5
|
+
import json
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from .client import OpenCodeClient
|
|
9
|
+
from .config import OpenCodeConfig
|
|
10
|
+
from .logging import configure_logging
|
|
11
|
+
from .managers import MCPManager, SessionManager, ToolManager
|
|
12
|
+
from .orchestrator import EventStreamer, MultiAgentRunner, SingleAgentRunner
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _build_runtime(config: OpenCodeConfig) -> dict[str, Any]:
|
|
16
|
+
client = OpenCodeClient(config)
|
|
17
|
+
session_manager = SessionManager(client)
|
|
18
|
+
tool_manager = ToolManager(client)
|
|
19
|
+
mcp_manager = MCPManager(client)
|
|
20
|
+
return {
|
|
21
|
+
"client": client,
|
|
22
|
+
"single": SingleAgentRunner(client=client, config=config, session_manager=session_manager, tool_manager=tool_manager, mcp_manager=mcp_manager),
|
|
23
|
+
"multi": MultiAgentRunner(client=client, config=config, session_manager=session_manager, tool_manager=tool_manager, mcp_manager=mcp_manager),
|
|
24
|
+
"session_manager": session_manager,
|
|
25
|
+
"tool_manager": tool_manager,
|
|
26
|
+
"mcp_manager": mcp_manager,
|
|
27
|
+
"event_streamer": EventStreamer(client),
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _to_json(payload: Any) -> str:
|
|
32
|
+
def _default(obj: Any) -> Any:
|
|
33
|
+
if is_dataclass(obj):
|
|
34
|
+
return asdict(obj)
|
|
35
|
+
return str(obj)
|
|
36
|
+
return json.dumps(payload, indent=2, default=_default)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _print_json(payload: Any) -> None:
|
|
40
|
+
print(_to_json(payload))
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
44
|
+
parser = argparse.ArgumentParser(
|
|
45
|
+
prog="opencode-agent",
|
|
46
|
+
description="Python orchestration CLI for the OpenCode Agent API.",
|
|
47
|
+
)
|
|
48
|
+
parser.add_argument("--base-url", help="Override OpenCode base URL")
|
|
49
|
+
parser.add_argument("--project-path", help="Override OpenCode project path")
|
|
50
|
+
parser.add_argument("--verbose", action="store_true", help="Enable verbose logging")
|
|
51
|
+
parser.add_argument("--json", dest="output_json", action="store_true", help="Output raw JSON instead of plain text")
|
|
52
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
53
|
+
|
|
54
|
+
run_parser = subparsers.add_parser("run", help="Run a single-agent task")
|
|
55
|
+
run_parser.add_argument("prompt")
|
|
56
|
+
run_parser.add_argument("--agent")
|
|
57
|
+
run_parser.add_argument("--model")
|
|
58
|
+
run_parser.add_argument("--session-id")
|
|
59
|
+
run_parser.add_argument("--title")
|
|
60
|
+
run_parser.add_argument("--tool", action="append", default=[])
|
|
61
|
+
run_parser.add_argument("--mcp", action="append", default=[])
|
|
62
|
+
run_parser.add_argument("--stream", action="store_true")
|
|
63
|
+
run_parser.add_argument("--async", dest="async_mode", action="store_true")
|
|
64
|
+
|
|
65
|
+
orchestration_parser = subparsers.add_parser("orchestrate", help="Run a multi-agent task")
|
|
66
|
+
orchestration_parser.add_argument("prompt")
|
|
67
|
+
orchestration_parser.add_argument("--strategy", default="plan-explore-build-review")
|
|
68
|
+
orchestration_parser.add_argument("--session-id")
|
|
69
|
+
orchestration_parser.add_argument("--title")
|
|
70
|
+
orchestration_parser.add_argument("--tool", action="append", default=[])
|
|
71
|
+
orchestration_parser.add_argument("--mcp", action="append", default=[])
|
|
72
|
+
orchestration_parser.add_argument("--parallel", action="store_true")
|
|
73
|
+
|
|
74
|
+
sessions_parser = subparsers.add_parser("sessions", help="Inspect sessions")
|
|
75
|
+
sessions_subparsers = sessions_parser.add_subparsers(dest="sessions_command", required=True)
|
|
76
|
+
sessions_subparsers.add_parser("list", help="List sessions")
|
|
77
|
+
status_parser = sessions_subparsers.add_parser("status", help="Show session status")
|
|
78
|
+
status_parser.add_argument("session_id", nargs="?")
|
|
79
|
+
abort_parser = sessions_subparsers.add_parser("abort", help="Abort a session")
|
|
80
|
+
abort_parser.add_argument("session_id")
|
|
81
|
+
|
|
82
|
+
tool_parser = subparsers.add_parser("tools", help="Inspect tools")
|
|
83
|
+
tool_parser.add_argument("--provider")
|
|
84
|
+
tool_parser.add_argument("--model")
|
|
85
|
+
tool_parser.add_argument("--ids", action="store_true")
|
|
86
|
+
|
|
87
|
+
mcp_parser = subparsers.add_parser("mcp", help="Inspect or register MCP servers")
|
|
88
|
+
mcp_subparsers = mcp_parser.add_subparsers(dest="mcp_command", required=True)
|
|
89
|
+
mcp_subparsers.add_parser("list", help="List MCP servers")
|
|
90
|
+
register_parser = mcp_subparsers.add_parser("register", help="Register an MCP server")
|
|
91
|
+
register_parser.add_argument("name")
|
|
92
|
+
register_parser.add_argument("config_json")
|
|
93
|
+
|
|
94
|
+
shell_parser = subparsers.add_parser("shell", help="Run a shell command inside a session")
|
|
95
|
+
shell_parser.add_argument("session_id")
|
|
96
|
+
shell_parser.add_argument("command_text")
|
|
97
|
+
shell_parser.add_argument("--agent")
|
|
98
|
+
shell_parser.add_argument("--model")
|
|
99
|
+
|
|
100
|
+
stream_parser = subparsers.add_parser("stream", help="Read SSE events")
|
|
101
|
+
stream_parser.add_argument("--limit", type=int, default=10)
|
|
102
|
+
|
|
103
|
+
return parser
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def main(argv: list[str] | None = None) -> int:
|
|
107
|
+
parser = build_parser()
|
|
108
|
+
args = parser.parse_args(argv)
|
|
109
|
+
config = OpenCodeConfig.from_env()
|
|
110
|
+
if args.base_url:
|
|
111
|
+
config.base_url = args.base_url
|
|
112
|
+
if args.project_path:
|
|
113
|
+
config.project_path = args.project_path
|
|
114
|
+
if args.verbose:
|
|
115
|
+
config.verbose = True
|
|
116
|
+
configure_logging(config.verbose)
|
|
117
|
+
runtime = _build_runtime(config)
|
|
118
|
+
client: OpenCodeClient = runtime["client"]
|
|
119
|
+
output_json: bool = args.output_json
|
|
120
|
+
try:
|
|
121
|
+
if args.command == "run":
|
|
122
|
+
if args.async_mode:
|
|
123
|
+
result = runtime["single"].run_async(
|
|
124
|
+
args.prompt,
|
|
125
|
+
agent=args.agent,
|
|
126
|
+
model=args.model,
|
|
127
|
+
session_id=args.session_id,
|
|
128
|
+
title=args.title,
|
|
129
|
+
tools=args.tool or None,
|
|
130
|
+
)
|
|
131
|
+
# async just returns metadata — always show as JSON
|
|
132
|
+
_print_json(result)
|
|
133
|
+
return 0
|
|
134
|
+
result = runtime["single"].run(
|
|
135
|
+
args.prompt,
|
|
136
|
+
agent=args.agent,
|
|
137
|
+
model=args.model,
|
|
138
|
+
session_id=args.session_id,
|
|
139
|
+
title=args.title,
|
|
140
|
+
tools=args.tool or None,
|
|
141
|
+
mcp_servers=args.mcp or None,
|
|
142
|
+
streaming=args.stream,
|
|
143
|
+
)
|
|
144
|
+
if output_json:
|
|
145
|
+
_print_json({"output": result.output, "metadata": result.metadata})
|
|
146
|
+
else:
|
|
147
|
+
print(result.output)
|
|
148
|
+
return 0
|
|
149
|
+
|
|
150
|
+
if args.command == "orchestrate":
|
|
151
|
+
result = runtime["multi"].run(
|
|
152
|
+
args.prompt,
|
|
153
|
+
strategy=args.strategy,
|
|
154
|
+
session_id=args.session_id,
|
|
155
|
+
title=args.title,
|
|
156
|
+
tools=args.tool or None,
|
|
157
|
+
mcp_servers=args.mcp or None,
|
|
158
|
+
parallel=args.parallel,
|
|
159
|
+
)
|
|
160
|
+
if output_json:
|
|
161
|
+
_print_json(result)
|
|
162
|
+
else:
|
|
163
|
+
print(result.output)
|
|
164
|
+
return 0
|
|
165
|
+
|
|
166
|
+
if args.command == "sessions":
|
|
167
|
+
if args.sessions_command == "list":
|
|
168
|
+
_print_json(client.list_sessions())
|
|
169
|
+
return 0
|
|
170
|
+
if args.sessions_command == "status":
|
|
171
|
+
status = client.get_session_status()
|
|
172
|
+
if args.session_id:
|
|
173
|
+
_print_json(status.get(args.session_id))
|
|
174
|
+
else:
|
|
175
|
+
_print_json(status)
|
|
176
|
+
return 0
|
|
177
|
+
if args.sessions_command == "abort":
|
|
178
|
+
client.abort_session(args.session_id)
|
|
179
|
+
print(f"aborted {args.session_id}")
|
|
180
|
+
return 0
|
|
181
|
+
|
|
182
|
+
if args.command == "tools":
|
|
183
|
+
_print_json(client.list_tool_ids() if args.ids else client.list_tools(provider=args.provider, model=args.model))
|
|
184
|
+
return 0
|
|
185
|
+
|
|
186
|
+
if args.command == "mcp":
|
|
187
|
+
if args.mcp_command == "list":
|
|
188
|
+
_print_json(runtime["mcp_manager"].list_servers())
|
|
189
|
+
return 0
|
|
190
|
+
if args.mcp_command == "register":
|
|
191
|
+
_print_json(runtime["mcp_manager"].register(args.name, json.loads(args.config_json)))
|
|
192
|
+
return 0
|
|
193
|
+
|
|
194
|
+
if args.command == "shell":
|
|
195
|
+
result = runtime["single"].run_shell(args.command_text, session_id=args.session_id, agent=args.agent, model=args.model)
|
|
196
|
+
if output_json:
|
|
197
|
+
_print_json(result)
|
|
198
|
+
else:
|
|
199
|
+
print(result.output)
|
|
200
|
+
return 0
|
|
201
|
+
|
|
202
|
+
if args.command == "stream":
|
|
203
|
+
_print_json(runtime["event_streamer"].stream(limit=args.limit))
|
|
204
|
+
return 0
|
|
205
|
+
|
|
206
|
+
return 1
|
|
207
|
+
finally:
|
|
208
|
+
client.close()
|