Skip to content

Commit b456dd7

Browse files
zhanghanduoclaude
andcommitted
chore(deps): move to apodex-agent-core 0.12.0 and drop 0.11.1 workarounds
- Pin apodex-agent-core==0.12.0. - infra/openai_client.py is a plain alias again (AgentCore treats an empty api_key as unset); only the session-affinity resolvers are configured. - TaskBoardObserver aliases the shared class (now critical upstream). - loop_types no longer hand re-exports wall_deadline_remaining_s (in __all__ upstream). - create_file annotates its rows/data shorthand shapes explicitly instead of patching items to {}, so every schema node keeps a concrete type for strict validators. - create_subagent uses the public bind_max_tokens instead of _ensure_bound. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent 7087729 commit b456dd7

11 files changed

Lines changed: 47 additions & 63 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Initial open-source release of FrontierAgent.
1212
### Changed
1313

1414
- **Runtime engine moved to [`apodex-agent-core`](https://pypi.org/project/apodex-agent-core/)
15-
(pinned `==0.11.1`).** The agent loop, loop contracts, tool execution,
15+
(pinned `==0.12.0`).** The agent loop, loop contracts, tool execution,
1616
compaction, observers, AgentBus, DAG and providers now come from `agent_core`;
1717
`frontier_agent.*` keeps its import paths as `sys.modules` aliases or thin
1818
adapters, so workflows, apodex and benchmarks are unchanged. Product policy is
@@ -22,7 +22,7 @@ Initial open-source release of FrontierAgent.
2222
follows AgentCore where the fork had diverged, notably: compaction pins the
2323
first user message verbatim and replaces legacy prose spill indexes, and
2424
`Any`-typed tool parameters generate `{"type": "string"}` (`create_file`
25-
restores open `items` for its shorthand arrays).
25+
now annotates its `rows` / `data` shorthand shapes explicitly).
2626

2727
### Added
2828

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,9 @@
1-
"""Task-board reminders (shared ``TaskBoardObserver`` with product policy)."""
1+
# pyright: reportWildcardImportFromLibrary=false
2+
"""Task-board reminders (implemented by ``agent_core.components.observers.task_board``)."""
23

3-
from agent_core.components.observers.task_board import TaskBoardObserver as _TaskBoardObserver
4+
import sys
45

6+
import agent_core.components.observers.task_board as _implementation
7+
from agent_core.components.observers.task_board import * # noqa: F403
58

6-
class TaskBoardObserver(_TaskBoardObserver):
7-
"""Critical, so the board reminder is collected into the next turn.
8-
9-
AgentCore defaults this observer to non-critical, where return values are
10-
dropped and the reminder would never reach the model.
11-
"""
12-
13-
critical: bool = True
14-
15-
16-
__all__ = ["TaskBoardObserver"]
9+
sys.modules[__name__] = _implementation

‎frontier_agent/core/loop_types.py‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,5 @@
55

66
import agent_core.loop_types as _implementation
77
from agent_core.loop_types import * # noqa: F403
8-
from agent_core.loop_types import ( # not in __all__; named for static checkers
9-
wall_deadline_remaining_s as wall_deadline_remaining_s,
10-
)
118

129
sys.modules[__name__] = _implementation

‎frontier_agent/core/runtime/loop/_bind.py‎

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,6 @@
77
from agent_core.runtime.loop._bind import (
88
_BoundLLM as _BoundLLM,
99
)
10-
from agent_core.runtime.loop._bind import (
11-
_ensure_bound as _ensure_bound,
12-
)
1310
from agent_core.runtime.loop._bind import (
1411
bind_max_tokens,
1512
bind_temperature,
@@ -34,7 +31,6 @@ def bind_session_id(llm: Any, task_id: str) -> Any:
3431

3532
__all__ = [
3633
"_BoundLLM",
37-
"_ensure_bound",
3834
"bind_max_tokens",
3935
"bind_session_id",
4036
"bind_temperature",

‎frontier_agent/core/runtime/loop/llm_client.py‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
from agent_core.tokens import estimate_message_tokens, estimate_text_tokens
2222

2323
from frontier_agent.core.runtime.loop._bind import (
24-
_ensure_bound,
2524
bind_max_tokens,
2625
bind_session_id,
2726
bind_temperature,
@@ -37,7 +36,6 @@
3736
"LLMReasoningRunaway",
3837
"LLMStreamStalled",
3938
"ThinkTagSplitter",
40-
"_ensure_bound",
4139
"bind_max_tokens",
4240
"bind_session_id",
4341
"bind_temperature",
Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# pyright: reportWildcardImportFromLibrary=false
12
"""FrontierAgent composition of AgentCore's OpenAI Chat Completions client.
23
34
Importing this module wires FrontierAgent's session-affinity policy into the
@@ -6,27 +7,14 @@
67
directly before this import silently drops the session id.
78
"""
89

9-
from __future__ import annotations
10-
11-
from typing import Any
10+
import sys
1211

1312
import agent_core.providers.openai_chat as _implementation
13+
from agent_core.providers.openai_chat import * # noqa: F403
1414

1515
from frontier_agent.infra.session_context import get_task_session_id, mirror_session_query
1616

1717
_implementation.configure_session_query_resolver(mirror_session_query)
1818
_implementation.configure_session_scope_resolver(get_task_session_id)
1919

20-
21-
class OpenAIClient(_implementation.OpenAIClient):
22-
"""Treat an empty ``api_key`` as unspecified.
23-
24-
The OpenAI SDK only consults ``OPENAI_API_KEY`` when ``api_key`` is None;
25-
a cached config can legitimately hold the pre-environment empty string.
26-
"""
27-
28-
def __init__(self, model: str, *, api_key: str | None = None, **kwargs: Any) -> None:
29-
super().__init__(model, api_key=api_key or None, **kwargs)
30-
31-
32-
__all__ = ["OpenAIClient"]
20+
sys.modules[__name__] = _implementation

‎plugins/tools/create_file.py‎

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@
2727

2828
logger = logging.getLogger(__name__)
2929

30+
# Shorthand payload shapes, spelled out so every schema node carries a concrete
31+
# ``type``: strict function-schema validators reject an untyped property, and
32+
# ``Any`` would degrade to ``string`` and reject nested rows instead.
33+
_Scalar = str | int | float | bool
34+
_Rows = list[list[_Scalar] | dict[str, Any]]
35+
_JsonArray = list[dict[str, Any] | list[_Scalar] | _Scalar]
36+
3037
_WRITER_SRC = writer_src()
3138
_DOC_EXTS = {"docx", "xlsx", "pptx"}
3239
_TEXT_EXTS = {"txt", "md", "csv", "tsv", "json", "jsonl", "html", "htm"}
@@ -210,8 +217,8 @@ def desugar_text_shorthand(
210217
*,
211218
ops: list[dict[str, Any]] | str | None,
212219
content: str | None,
213-
rows: list[Any] | str | None,
214-
data: dict[str, Any] | list[Any] | str | None,
220+
rows: _Rows | str | None,
221+
data: dict[str, Any] | _JsonArray | str | None,
215222
overwrite: bool = False,
216223
) -> tuple[list[dict[str, Any]] | str | None, str]:
217224
"""Fold a top-level ``content``/``rows``/``data`` into one ``create`` op.
@@ -258,8 +265,8 @@ async def create_file(
258265
path: str,
259266
ops: list[dict[str, Any]] | str | None = None,
260267
content: str | None = None,
261-
rows: list[Any] | str | None = None,
262-
data: dict[str, Any] | list[Any] | str | None = None,
268+
rows: _Rows | str | None = None,
269+
data: dict[str, Any] | _JsonArray | str | None = None,
263270
overwrite: bool = False,
264271
) -> str:
265272
"""Create or edit a deliverable file in the sandbox — office (docx/xlsx/pptx)
@@ -547,11 +554,3 @@ async def create_file(
547554
)
548555
return result.stdout or "(no output)"
549556

550-
551-
# The schema generator maps ``Any`` to ``{"type": "string"}``, which would make
552-
# schema-aware providers reject nested csv rows and JSON arrays. Items of these
553-
# shorthand arrays are genuinely open.
554-
for _name in ("rows", "data"):
555-
for _option in create_file.parameters["properties"][_name].get("anyOf", []):
556-
if _option.get("type") == "array":
557-
_option["items"] = {}

‎plugins/tools/create_subagent.py‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -434,10 +434,8 @@ def _bind_sub_agent_llm(runtime: Any | None) -> Any | None:
434434
eff_max_tokens,
435435
)
436436
try:
437-
from dataclasses import replace
438-
439-
from frontier_agent.core.runtime.loop.llm_client import _ensure_bound
440-
bound = replace(_ensure_bound(llm), max_tokens=eff_max_tokens)
437+
from frontier_agent.core.runtime.loop.llm_client import bind_max_tokens
438+
bound = bind_max_tokens(llm, eff_max_tokens)
441439
except Exception:
442440
bound = llm
443441
stream_cfg = getattr(runtime, "stream_repetition_config", None)

‎pyproject.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ requires-python = ">=3.12"
88
dependencies = [
99
# Runtime engine (agent loop, tools, observers, sub-agents, DAG). Pinned exactly:
1010
# 0.x MINOR bumps may be breaking.
11-
"apodex-agent-core==0.11.1",
11+
"apodex-agent-core==0.12.0",
1212
"pydantic>=2.0",
1313
"pydantic-settings>=2.14.2",
1414
"openai>=1.50",

‎tests/test_create_file_shorthand.py‎

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,23 @@ def test_the_tool_schema_accepts_the_advertised_json_shapes() -> None:
6767
if option.get("type") == "array"
6868
)
6969

70-
assert rows_array["items"] == {}
71-
assert data_array["items"] == {}
70+
# Nested csv rows and jsonl objects must both validate.
71+
row_shapes = {option["type"] for option in rows_array["items"]["anyOf"]}
72+
assert row_shapes == {"array", "object"}
73+
data_shapes = {option["type"] for option in data_array["items"]["anyOf"]}
74+
assert {"array", "object", "string"} <= data_shapes
75+
76+
# Strict function-schema validators reject a node without a ``type``.
77+
def untyped(node: object) -> list[object]:
78+
if isinstance(node, dict):
79+
own = [node] if "type" not in node and "anyOf" not in node else []
80+
return own + [bad for child in node.values() for bad in untyped(child)]
81+
if isinstance(node, list):
82+
return [bad for child in node for bad in untyped(child)]
83+
return []
84+
85+
assert untyped(properties["rows"]["anyOf"]) == []
86+
assert untyped(properties["data"]["anyOf"]) == []
7287

7388

7489
def test_empty_string_content_still_folds() -> None:

0 commit comments

Comments
 (0)