Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion schema/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
refs/tags/schema-v1.19.0
refs/tags/schema-v1.21.0
471 changes: 267 additions & 204 deletions schema/schema.json

Large diffs are not rendered by default.

22 changes: 1 addition & 21 deletions scripts/gen_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
from __future__ import annotations

import argparse
import copy
import difflib
import json
import subprocess
Expand Down Expand Up @@ -34,13 +33,6 @@
"integer+uint64=integer",
)

OPEN_UNIONS = {
"CreateElicitationRequest": 2,
"CreateElicitationResponse": 3,
"ElicitationPropertySchema": 5,
"MultiSelectItems": 1,
}


def _inline_model_ref(definition: str, *steps: tuple[str, int | None]) -> str:
ref = f"#/$defs/{definition}"
Expand Down Expand Up @@ -312,7 +304,7 @@ def render_schema() -> str:
if not SCHEMA_JSON.exists():
raise FileNotFoundError("schema/schema.json is missing; fetch a pinned schema release first")

schema = _schema_for_codegen(json.loads(SCHEMA_JSON.read_text(encoding="utf-8")))
schema = json.loads(SCHEMA_JSON.read_text(encoding="utf-8"))
generated = generate(
schema,
input_file_type=InputFileType.JsonSchema,
Expand Down Expand Up @@ -348,18 +340,6 @@ def render_schema() -> str:
return _format_python(f"{generated.rstrip()}\n\n\n{COMPATIBILITY_ALIASES}\n")


def _schema_for_codegen(schema: dict[str, Any]) -> dict[str, Any]:
"""Drop open-union constraints that Pydantic cannot represent statically."""
patched = copy.deepcopy(schema)
for name, catchall_index in OPEN_UNIONS.items():
try:
del patched["$defs"][name]["discriminator"]
del patched["$defs"][name]["anyOf"][catchall_index]["not"]
except KeyError:
raise ValueError(f"{name} no longer has the expected open-union shape") from None
return patched


def _build_validators_config(schema: dict[str, Any]) -> dict[str, ModelValidators]:
validators: dict[str, list[ValidatorDefinition]] = {
"InitializeRequest": [
Expand Down
14 changes: 9 additions & 5 deletions scripts/gen_signature.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import importlib.util
import inspect
import sys
import types
import typing as t
from pathlib import Path

Expand Down Expand Up @@ -35,7 +36,6 @@ class NodeTransformer(ast.NodeTransformer):
def __init__(self) -> None:
self._type_import_node: ast.ImportFrom | None = None
self._schema_import_node: ast.ImportFrom | None = None
self._should_rewrite = False
self._literals = {name: value for name, value in schema.__dict__.items() if t.get_origin(value) is t.Literal}
self._current_model_name: str | None = None

Expand All @@ -44,21 +44,20 @@ def _add_typing_import(self, name: str) -> None:
return
if not any(alias.name == name for alias in self._type_import_node.names):
self._type_import_node.names.append(ast.alias(name=name))
self._should_rewrite = True

def _add_schema_import(self, name: str) -> None:
if not self._schema_import_node:
return
if not any(alias.name == name for alias in self._schema_import_node.names):
self._schema_import_node.names.append(ast.alias(name=name))
self._should_rewrite = True

def transform(self, source_file: Path) -> None:
with source_file.open("r", encoding="utf-8") as f:
source_code = f.read()
tree = ast.parse(source_code)
before = ast.dump(tree, include_attributes=False)
self.visit(tree)
if self._should_rewrite:
if ast.dump(tree, include_attributes=False) != before:
print("Rewriting signatures in", source_file)
new_code = ast.unparse(tree)
with source_file.open("w", encoding="utf-8") as f:
Expand Down Expand Up @@ -90,7 +89,6 @@ def visit_func(self, node: ast.FunctionDef | ast.AsyncFunctionDef) -> ast.AST:
)
if not decorator:
return self.generic_visit(node)
self._should_rewrite = True
model_name = t.cast(ast.Name, decorator.args[0]).id
model = t.cast(type[schema.BaseModel], getattr(schema, model_name))
self._current_model_name = model_name
Expand Down Expand Up @@ -130,6 +128,12 @@ def _format_annotation(self, annotation: t.Any) -> ast.expr:
origin = t.get_origin(annotation)
if origin is t.Annotated:
return self._format_annotation(t.get_args(annotation)[0])
if origin in (t.Union, types.UnionType):
first, *rest = t.get_args(annotation)
formatted = self._format_annotation(first)
for argument in rest:
formatted = ast.BinOp(left=formatted, op=ast.BitOr(), right=self._format_annotation(argument))
return formatted
if origin is t.Literal and annotation in self._literals.values():
name = next(name for name, value in self._literals.items() if value is annotation)
self._add_schema_import(name)
Expand Down
6 changes: 5 additions & 1 deletion src/acp/agent/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@
RequestPermissionResponse,
SessionInfoUpdate,
SessionNotification,
SessionUpdateCompactionSummaryChunk,
SessionUpdateCompactionUpdate,
TerminalOutputRequest,
TerminalOutputResponse,
ToolCallProgress,
Expand Down Expand Up @@ -121,7 +123,9 @@ async def session_update(
| CurrentModeUpdate
| ConfigOptionUpdate
| SessionInfoUpdate
| UsageUpdate,
| UsageUpdate
| SessionUpdateCompactionUpdate
| SessionUpdateCompactionSummaryChunk,
**kwargs: Any,
) -> None:
await notify_model(
Expand Down
14 changes: 14 additions & 0 deletions src/acp/contrib/tool_calls.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class TrackedToolCallView(BaseModel):

tool_call_id: str
title: str | None
name: str | None
kind: ToolKind | None
status: ToolCallStatus | None
content: tuple[Any, ...] | None
Expand All @@ -70,6 +71,7 @@ def __init__(
*,
tool_call_id: str,
title: str | None = None,
name: str | None = None,
kind: ToolKind | None = None,
status: ToolCallStatus | None = None,
content: Sequence[Any] | None = None,
Expand All @@ -79,6 +81,7 @@ def __init__(
) -> None:
self.tool_call_id = tool_call_id
self.title = title
self.name = name
self.kind = kind
self.status = status
self.content = _copy_model_list(content)
Expand All @@ -91,6 +94,7 @@ def to_view(self) -> TrackedToolCallView:
return TrackedToolCallView(
tool_call_id=self.tool_call_id,
title=self.title,
name=self.name,
kind=self.kind,
status=self.status,
content=tuple(item.model_copy(deep=True) for item in self.content) if self.content else None,
Expand All @@ -103,6 +107,7 @@ def to_tool_call_model(self) -> ToolCallUpdate:
return ToolCallUpdate(
tool_call_id=self.tool_call_id,
title=self.title,
name=self.name,
kind=self.kind,
status=self.status,
content=_copy_model_list(self.content),
Expand All @@ -118,6 +123,7 @@ def to_start_model(self) -> ToolCallStart:
session_update="tool_call",
tool_call_id=self.tool_call_id,
title=self.title,
name=self.name,
kind=self.kind,
status=self.status,
content=_copy_model_list(self.content),
Expand All @@ -130,6 +136,7 @@ def update(
self,
*,
title: Any = UNSET,
name: Any = UNSET,
kind: Any = UNSET,
status: Any = UNSET,
content: Any = UNSET,
Expand All @@ -141,6 +148,9 @@ def update(
if title is not UNSET:
self.title = cast(str | None, title)
kwargs["title"] = self.title
if name is not UNSET:
self.name = cast(str | None, name)
kwargs["name"] = self.name
if kind is not UNSET:
self.kind = cast(ToolKind | None, kind)
kwargs["kind"] = self.kind
Expand Down Expand Up @@ -190,6 +200,7 @@ def start(
external_id: str,
*,
title: str,
name: str | None = None,
kind: ToolKind | None = None,
status: ToolCallStatus | None = "in_progress",
content: Sequence[Any] | None = None,
Expand All @@ -202,6 +213,7 @@ def start(
state = _TrackedToolCall(
tool_call_id=call_id,
title=title,
name=name,
kind=kind,
status=status,
content=content,
Expand All @@ -217,6 +229,7 @@ def progress(
external_id: str,
*,
title: Any = UNSET,
name: Any = UNSET,
kind: Any = UNSET,
status: Any = UNSET,
content: Any = UNSET,
Expand All @@ -228,6 +241,7 @@ def progress(
state = self._require_call(external_id)
return state.update(
title=title,
name=name,
kind=kind,
status=status,
content=content,
Expand Down
6 changes: 5 additions & 1 deletion src/acp/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@
ResumeSessionResponse,
SessionInfoUpdate,
SessionNotification,
SessionUpdateCompactionSummaryChunk,
SessionUpdateCompactionUpdate,
SetSessionConfigOptionBooleanRequest,
SetSessionConfigOptionResponse,
SetSessionConfigOptionSelectRequest,
Expand Down Expand Up @@ -102,7 +104,9 @@ async def session_update(
| CurrentModeUpdate
| ConfigOptionUpdate
| SessionInfoUpdate
| UsageUpdate,
| UsageUpdate
| SessionUpdateCompactionUpdate
| SessionUpdateCompactionSummaryChunk,
**kwargs: Any,
) -> None: ...

Expand Down
2 changes: 1 addition & 1 deletion src/acp/meta.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Generated from schema/meta.json. Do not edit by hand.
# Schema ref: refs/tags/schema-v1.19.0
# Schema ref: refs/tags/schema-v1.21.0
AGENT_METHODS = {
"initialize": "initialize",
"authenticate": "authenticate",
Expand Down
Loading
Loading