From d69c367285d8c33c210258244ef9045b9e55d418 Mon Sep 17 00:00:00 2001 From: Hare Date: Sun, 23 Aug 2026 03:41:51 +0900 Subject: [PATCH] feat: prepare agen crates for publication --- Cargo.lock | 88 ++-- Cargo.toml | 12 +- crates/agen-macros/Cargo.toml | 25 + crates/agen-macros/LICENSE | 7 + crates/agen-macros/README.md | 32 ++ crates/agen-macros/src/lib.rs | 482 ++++++++++++++++++ crates/{llm-engine => agen}/Cargo.toml | 28 +- crates/agen/LICENSE | 7 + crates/agen/README.md | 90 ++++ crates/agen/docs/architecture.md | 62 +++ crates/agen/docs/requirements.md | 39 ++ .../examples/engine_cancel_demo.rs | 6 +- .../examples/engine_cli.rs | 10 +- .../examples/record_test_fixtures/main.rs | 6 +- .../examples/record_test_fixtures/recorder.rs | 2 +- .../record_test_fixtures/scenarios.rs | 2 +- crates/{llm-engine => agen}/src/callback.rs | 0 crates/{llm-engine => agen}/src/engine.rs | 11 +- crates/{llm-engine => agen}/src/event.rs | 0 crates/{llm-engine => agen}/src/handler.rs | 2 +- .../{llm-engine => agen}/src/interceptor.rs | 0 crates/agen/src/lib.rs | 42 ++ .../src/llm_client/auth.rs | 2 +- .../src/llm_client/capability.rs | 0 .../src/llm_client/client.rs | 0 .../src/llm_client/error.rs | 0 .../src/llm_client/event.rs | 0 .../src/llm_client/mod.rs | 0 .../src/llm_client/retry.rs | 2 +- .../llm_client/scheme/anthropic/capability.rs | 0 .../src/llm_client/scheme/anthropic/events.rs | 0 .../src/llm_client/scheme/anthropic/mod.rs | 0 .../llm_client/scheme/anthropic/request.rs | 0 .../scheme/anthropic/scheme_impl.rs | 0 .../llm_client/scheme/gemini/capability.rs | 0 .../src/llm_client/scheme/gemini/events.rs | 0 .../src/llm_client/scheme/gemini/mod.rs | 0 .../src/llm_client/scheme/gemini/request.rs | 0 .../llm_client/scheme/gemini/scheme_impl.rs | 0 .../src/llm_client/scheme/mod.rs | 0 .../scheme/openai_chat/capability.rs | 0 .../llm_client/scheme/openai_chat/events.rs | 0 .../src/llm_client/scheme/openai_chat/mod.rs | 0 .../llm_client/scheme/openai_chat/request.rs | 0 .../scheme/openai_chat/scheme_impl.rs | 0 .../scheme/openai_responses/capability.rs | 0 .../scheme/openai_responses/events.rs | 0 .../llm_client/scheme/openai_responses/mod.rs | 4 +- .../scheme/openai_responses/request.rs | 0 .../scheme/openai_responses/scheme_impl.rs | 0 .../src/llm_client/transport.rs | 0 .../src/llm_client/types.rs | 2 +- crates/{llm-engine => agen}/src/message.rs | 0 .../src/providers/codex/auth_json.rs | 0 .../src/providers/codex/error.rs | 0 .../src/providers/codex/jwt.rs | 0 .../src/providers/codex/mod.rs | 0 .../src/providers/codex/refresh.rs | 0 .../{llm-engine => agen}/src/providers/mod.rs | 0 crates/{llm-engine => agen}/src/prune.rs | 0 crates/{llm-engine => agen}/src/state.rs | 2 +- .../src/timeline/event.rs | 0 .../{llm-engine => agen}/src/timeline/mod.rs | 0 .../src/timeline/text_block_collector.rs | 0 .../src/timeline/thinking_block_collector.rs | 0 .../src/timeline/timeline.rs | 2 +- .../src/timeline/tool_call_collector.rs | 0 .../{llm-engine => agen}/src/token_counter.rs | 0 crates/{llm-engine => agen}/src/tool.rs | 2 +- .../{llm-engine => agen}/src/tool_server.rs | 0 .../{llm-engine => agen}/src/usage_record.rs | 0 .../tests/anthropic_fixtures.rs | 0 .../tests/callback_test.rs | 15 +- .../{llm-engine => agen}/tests/common/mod.rs | 8 +- .../tests/compile_fail.rs | 0 .../tests/engine_fixtures.rs | 16 +- .../tests/engine_state_test.rs | 10 +- .../anthropic/anthropic_1767624445.jsonl | 0 .../fixtures/anthropic/simple_text.jsonl | 0 .../tests/fixtures/anthropic/tool_call.jsonl | 0 .../tests/fixtures/gemini/long_text.jsonl | 0 .../tests/fixtures/gemini/simple_text.jsonl | 0 .../tests/fixtures/gemini/tool_call.jsonl | 0 .../tests/fixtures/ollama/long_text.jsonl | 0 .../tests/fixtures/ollama/simple_text.jsonl | 0 .../tests/fixtures/ollama/tool_call.jsonl | 0 .../tests/fixtures/openai/long_text.jsonl | 0 .../fixtures/openai/openai_1767708975.jsonl | 0 .../tests/fixtures/openai/simple_text.jsonl | 0 .../tests/fixtures/openai/tool_call.jsonl | 0 .../tests/gemini_fixtures.rs | 0 .../tests/ollama_fixtures.rs | 0 .../tests/openai_fixtures.rs | 0 .../tests/parallel_execution_test.rs | 18 +- .../tests/reasoning_round_trip_test.rs | 14 +- .../tests/tool_macro_test.rs | 7 +- .../tests/transport_retry_test.rs | 16 +- .../tests/ui/locked_register_tool.rs | 10 +- .../tests/ui/locked_register_tool.stderr | 0 .../ui/tool_server_handle_register_tool.rs | 10 +- .../tool_server_handle_register_tool.stderr | 0 crates/client/README.md | 2 +- crates/llm-engine-macros/Cargo.toml | 14 - crates/llm-engine-macros/README.md | 27 - crates/llm-engine-macros/src/lib.rs | 344 ------------- crates/llm-engine/README.md | 32 -- crates/llm-engine/docs/architecture.md | 73 --- crates/llm-engine/docs/requirements.md | 48 -- crates/llm-engine/src/lib.rs | 66 --- crates/manifest/Cargo.toml | 2 +- crates/manifest/src/model.rs | 4 +- crates/manifest/src/model_catalog.rs | 2 +- crates/memory/Cargo.toml | 2 +- crates/memory/README.md | 2 +- crates/memory/src/extract/input.rs | 8 +- crates/memory/src/extract/tool.rs | 6 +- crates/plugin-pdk/tests/template.rs | 2 +- crates/protocol/README.md | 2 +- crates/protocol/src/lib.rs | 2 +- crates/session-metrics/README.md | 2 +- crates/session-store/Cargo.toml | 2 +- crates/session-store/src/event_trace.rs | 2 +- crates/session-store/src/lib.rs | 4 +- crates/session-store/src/logged_item.rs | 10 +- crates/session-store/src/segment.rs | 6 +- crates/session-store/src/segment_log.rs | 14 +- crates/session-store/src/system_item.rs | 2 +- crates/session-store/tests/common/mod.rs | 4 +- crates/session-store/tests/fs_store_test.rs | 10 +- crates/session-store/tests/session_test.rs | 20 +- crates/ticket/Cargo.toml | 2 +- crates/ticket/src/tool.rs | 38 +- crates/tools/Cargo.toml | 2 +- crates/tools/README.md | 2 +- crates/tools/src/bash.rs | 4 +- crates/tools/src/edit.rs | 4 +- crates/tools/src/error.rs | 2 +- crates/tools/src/glob.rs | 4 +- crates/tools/src/grep.rs | 4 +- crates/tools/src/lib.rs | 6 +- crates/tools/src/read.rs | 4 +- crates/tools/src/tracker.rs | 2 +- crates/tools/src/view_image.rs | 6 +- crates/tools/src/web.rs | 6 +- crates/tools/src/write.rs | 8 +- crates/tools/tests/edge_cases.rs | 4 +- crates/tools/tests/integration.rs | 14 +- crates/tui/Cargo.toml | 2 +- crates/tui/src/app.rs | 10 +- crates/tui/src/worker_list.rs | 2 +- crates/worker-runtime/Cargo.toml | 2 +- crates/worker-runtime/src/worker_backend.rs | 6 +- crates/worker/Cargo.toml | 2 +- crates/worker/README.md | 4 +- crates/worker/src/compact/prune.rs | 8 +- crates/worker/src/compact/token_counter.rs | 16 +- crates/worker/src/compact/usage_tracker.rs | 4 +- crates/worker/src/compact/worker.rs | 22 +- crates/worker/src/controller.rs | 4 +- crates/worker/src/discovery.rs | 8 +- crates/worker/src/feature.rs | 14 +- .../src/feature/builtin/flow_transition.rs | 22 +- .../src/feature/builtin/manage_workdir.rs | 4 +- .../src/feature/builtin/manage_worker.rs | 4 +- crates/worker/src/feature/builtin/memory.rs | 6 +- .../src/feature/builtin/memory_extract.rs | 10 +- .../src/feature/builtin/merge_request.rs | 2 +- .../worker/src/feature/builtin/objective.rs | 6 +- .../src/feature/builtin/orchestration.rs | 4 +- .../src/feature/builtin/session_explore.rs | 18 +- crates/worker/src/feature/builtin/task/mod.rs | 2 +- .../worker/src/feature/builtin/task/store.rs | 2 +- .../src/feature/builtin/task/tool_impl.rs | 10 +- crates/worker/src/feature/builtin/ticket.rs | 4 +- .../src/feature/builtin/worker_observation.rs | 18 +- crates/worker/src/feature/mcp.rs | 4 +- crates/worker/src/feature/plugin.rs | 6 +- crates/worker/src/fs_view.rs | 4 +- crates/worker/src/hook.rs | 10 +- crates/worker/src/internal_worker.rs | 8 +- crates/worker/src/interrupt_prep.rs | 2 +- crates/worker/src/ipc/interceptor.rs | 30 +- crates/worker/src/model_client.rs | 6 +- crates/worker/src/permission.rs | 2 +- crates/worker/src/segment_log_sink.rs | 2 +- crates/worker/src/session_capture.rs | 9 +- crates/worker/src/shutdown_after_idle.rs | 2 +- crates/worker/src/spawn/comm_tools.rs | 8 +- crates/worker/src/spawn/tool.rs | 28 +- crates/worker/src/worker.rs | 50 +- crates/worker/tests/compact_events_test.rs | 12 +- crates/worker/tests/controller_test.rs | 59 +-- crates/worker/tests/session_metrics_test.rs | 10 +- .../tests/system_prompt_template_test.rs | 6 +- crates/yoi/README.md | 2 +- docs/design/overview.md | 2 +- docs/design/provider-model-boundary.md | 2 +- docs/development/rust-testing-strategy.md | 2 +- ...26-05-24-compact-retained-tail-oversize.md | 2 +- ...ion-jsonl-partial-write-restore-failure.md | 2 +- docs/report/test-code-audit-20260806.md | 2 +- docs/report/test-validity-20260612/README.md | 4 +- .../{llm-worker-macros.md => agen-macros.md} | 28 +- .../{llm-worker.md => agen.md} | 18 +- docs/report/test-validity-20260612/pod.md | 2 +- .../test-validity-20260612/session-store.md | 2 +- 206 files changed, 1316 insertions(+), 1145 deletions(-) create mode 100644 crates/agen-macros/Cargo.toml create mode 100644 crates/agen-macros/LICENSE create mode 100644 crates/agen-macros/README.md create mode 100644 crates/agen-macros/src/lib.rs rename crates/{llm-engine => agen}/Cargo.toml (58%) create mode 100644 crates/agen/LICENSE create mode 100644 crates/agen/README.md create mode 100644 crates/agen/docs/architecture.md create mode 100644 crates/agen/docs/requirements.md rename crates/{llm-engine => agen}/examples/engine_cancel_demo.rs (91%) rename crates/{llm-engine => agen}/examples/engine_cli.rs (99%) rename crates/{llm-engine => agen}/examples/record_test_fixtures/main.rs (98%) rename crates/{llm-engine => agen}/examples/record_test_fixtures/recorder.rs (98%) rename crates/{llm-engine => agen}/examples/record_test_fixtures/scenarios.rs (97%) rename crates/{llm-engine => agen}/src/callback.rs (100%) rename crates/{llm-engine => agen}/src/engine.rs (99%) rename crates/{llm-engine => agen}/src/event.rs (100%) rename crates/{llm-engine => agen}/src/handler.rs (98%) rename crates/{llm-engine => agen}/src/interceptor.rs (100%) create mode 100644 crates/agen/src/lib.rs rename crates/{llm-engine => agen}/src/llm_client/auth.rs (98%) rename crates/{llm-engine => agen}/src/llm_client/capability.rs (100%) rename crates/{llm-engine => agen}/src/llm_client/client.rs (100%) rename crates/{llm-engine => agen}/src/llm_client/error.rs (100%) rename crates/{llm-engine => agen}/src/llm_client/event.rs (100%) rename crates/{llm-engine => agen}/src/llm_client/mod.rs (100%) rename crates/{llm-engine => agen}/src/llm_client/retry.rs (97%) rename crates/{llm-engine => agen}/src/llm_client/scheme/anthropic/capability.rs (100%) rename crates/{llm-engine => agen}/src/llm_client/scheme/anthropic/events.rs (100%) rename crates/{llm-engine => agen}/src/llm_client/scheme/anthropic/mod.rs (100%) rename crates/{llm-engine => agen}/src/llm_client/scheme/anthropic/request.rs (100%) rename crates/{llm-engine => agen}/src/llm_client/scheme/anthropic/scheme_impl.rs (100%) rename crates/{llm-engine => agen}/src/llm_client/scheme/gemini/capability.rs (100%) rename crates/{llm-engine => agen}/src/llm_client/scheme/gemini/events.rs (100%) rename crates/{llm-engine => agen}/src/llm_client/scheme/gemini/mod.rs (100%) rename crates/{llm-engine => agen}/src/llm_client/scheme/gemini/request.rs (100%) rename crates/{llm-engine => agen}/src/llm_client/scheme/gemini/scheme_impl.rs (100%) rename crates/{llm-engine => agen}/src/llm_client/scheme/mod.rs (100%) rename crates/{llm-engine => agen}/src/llm_client/scheme/openai_chat/capability.rs (100%) rename crates/{llm-engine => agen}/src/llm_client/scheme/openai_chat/events.rs (100%) rename crates/{llm-engine => agen}/src/llm_client/scheme/openai_chat/mod.rs (100%) rename crates/{llm-engine => agen}/src/llm_client/scheme/openai_chat/request.rs (100%) rename crates/{llm-engine => agen}/src/llm_client/scheme/openai_chat/scheme_impl.rs (100%) rename crates/{llm-engine => agen}/src/llm_client/scheme/openai_responses/capability.rs (100%) rename crates/{llm-engine => agen}/src/llm_client/scheme/openai_responses/events.rs (100%) rename crates/{llm-engine => agen}/src/llm_client/scheme/openai_responses/mod.rs (97%) rename crates/{llm-engine => agen}/src/llm_client/scheme/openai_responses/request.rs (100%) rename crates/{llm-engine => agen}/src/llm_client/scheme/openai_responses/scheme_impl.rs (100%) rename crates/{llm-engine => agen}/src/llm_client/transport.rs (100%) rename crates/{llm-engine => agen}/src/llm_client/types.rs (99%) rename crates/{llm-engine => agen}/src/message.rs (100%) rename crates/{llm-engine => agen}/src/providers/codex/auth_json.rs (100%) rename crates/{llm-engine => agen}/src/providers/codex/error.rs (100%) rename crates/{llm-engine => agen}/src/providers/codex/jwt.rs (100%) rename crates/{llm-engine => agen}/src/providers/codex/mod.rs (100%) rename crates/{llm-engine => agen}/src/providers/codex/refresh.rs (100%) rename crates/{llm-engine => agen}/src/providers/mod.rs (100%) rename crates/{llm-engine => agen}/src/prune.rs (100%) rename crates/{llm-engine => agen}/src/state.rs (98%) rename crates/{llm-engine => agen}/src/timeline/event.rs (100%) rename crates/{llm-engine => agen}/src/timeline/mod.rs (100%) rename crates/{llm-engine => agen}/src/timeline/text_block_collector.rs (100%) rename crates/{llm-engine => agen}/src/timeline/thinking_block_collector.rs (100%) rename crates/{llm-engine => agen}/src/timeline/timeline.rs (99%) rename crates/{llm-engine => agen}/src/timeline/tool_call_collector.rs (100%) rename crates/{llm-engine => agen}/src/token_counter.rs (100%) rename crates/{llm-engine => agen}/src/tool.rs (99%) rename crates/{llm-engine => agen}/src/tool_server.rs (100%) rename crates/{llm-engine => agen}/src/usage_record.rs (100%) rename crates/{llm-engine => agen}/tests/anthropic_fixtures.rs (100%) rename crates/{llm-engine => agen}/tests/callback_test.rs (96%) rename crates/{llm-engine => agen}/tests/common/mod.rs (96%) rename crates/{llm-engine => agen}/tests/compile_fail.rs (100%) rename crates/{llm-engine => agen}/tests/engine_fixtures.rs (94%) rename crates/{llm-engine => agen}/tests/engine_state_test.rs (98%) rename crates/{llm-engine => agen}/tests/fixtures/anthropic/anthropic_1767624445.jsonl (100%) rename crates/{llm-engine => agen}/tests/fixtures/anthropic/simple_text.jsonl (100%) rename crates/{llm-engine => agen}/tests/fixtures/anthropic/tool_call.jsonl (100%) rename crates/{llm-engine => agen}/tests/fixtures/gemini/long_text.jsonl (100%) rename crates/{llm-engine => agen}/tests/fixtures/gemini/simple_text.jsonl (100%) rename crates/{llm-engine => agen}/tests/fixtures/gemini/tool_call.jsonl (100%) rename crates/{llm-engine => agen}/tests/fixtures/ollama/long_text.jsonl (100%) rename crates/{llm-engine => agen}/tests/fixtures/ollama/simple_text.jsonl (100%) rename crates/{llm-engine => agen}/tests/fixtures/ollama/tool_call.jsonl (100%) rename crates/{llm-engine => agen}/tests/fixtures/openai/long_text.jsonl (100%) rename crates/{llm-engine => agen}/tests/fixtures/openai/openai_1767708975.jsonl (100%) rename crates/{llm-engine => agen}/tests/fixtures/openai/simple_text.jsonl (100%) rename crates/{llm-engine => agen}/tests/fixtures/openai/tool_call.jsonl (100%) rename crates/{llm-engine => agen}/tests/gemini_fixtures.rs (100%) rename crates/{llm-engine => agen}/tests/ollama_fixtures.rs (100%) rename crates/{llm-engine => agen}/tests/openai_fixtures.rs (100%) rename crates/{llm-engine => agen}/tests/parallel_execution_test.rs (98%) rename crates/{llm-engine => agen}/tests/reasoning_round_trip_test.rs (96%) rename crates/{llm-engine => agen}/tests/tool_macro_test.rs (98%) rename crates/{llm-engine => agen}/tests/transport_retry_test.rs (93%) rename crates/{llm-engine => agen}/tests/ui/locked_register_tool.rs (70%) rename crates/{llm-engine => agen}/tests/ui/locked_register_tool.stderr (100%) rename crates/{llm-engine => agen}/tests/ui/tool_server_handle_register_tool.rs (70%) rename crates/{llm-engine => agen}/tests/ui/tool_server_handle_register_tool.stderr (100%) delete mode 100644 crates/llm-engine-macros/Cargo.toml delete mode 100644 crates/llm-engine-macros/README.md delete mode 100644 crates/llm-engine-macros/src/lib.rs delete mode 100644 crates/llm-engine/README.md delete mode 100644 crates/llm-engine/docs/architecture.md delete mode 100644 crates/llm-engine/docs/requirements.md delete mode 100644 crates/llm-engine/src/lib.rs rename docs/report/test-validity-20260612/{llm-worker-macros.md => agen-macros.md} (74%) rename docs/report/test-validity-20260612/{llm-worker.md => agen.md} (94%) diff --git a/Cargo.lock b/Cargo.lock index 8894e0be..0cdaa477 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -37,6 +37,42 @@ dependencies = [ "cpufeatures 0.2.17", ] +[[package]] +name = "agen" +version = "0.2.1" +dependencies = [ + "agen-macros", + "async-trait", + "base64 0.22.1", + "chrono", + "clap", + "dotenv", + "eventsource-stream", + "futures", + "reqwest", + "schemars", + "serde", + "serde_json", + "tempfile", + "thiserror 2.0.18", + "tokio", + "tokio-util", + "tracing", + "tracing-subscriber", + "trybuild", + "wiremock", + "zstd", +] + +[[package]] +name = "agen-macros" +version = "0.2.0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "aho-corasick" version = "1.1.4" @@ -2261,42 +2297,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" -[[package]] -name = "llm-engine" -version = "0.2.1" -dependencies = [ - "async-trait", - "base64 0.22.1", - "chrono", - "clap", - "dotenv", - "eventsource-stream", - "futures", - "llm-engine-macros", - "reqwest", - "schemars", - "serde", - "serde_json", - "tempfile", - "thiserror 2.0.18", - "tokio", - "tokio-util", - "tracing", - "tracing-subscriber", - "trybuild", - "wiremock", - "zstd", -] - -[[package]] -name = "llm-engine-macros" -version = "0.2.0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "lock_api" version = "0.4.14" @@ -2384,8 +2384,8 @@ dependencies = [ name = "manifest" version = "0.1.0" dependencies = [ + "agen", "arc-swap", - "llm-engine", "protocol", "secrets", "serde", @@ -2512,11 +2512,11 @@ dependencies = [ name = "memory" version = "0.1.0" dependencies = [ + "agen", "async-trait", "chrono", "libc", "lint-common", - "llm-engine", "manifest", "schemars", "serde", @@ -3967,10 +3967,10 @@ dependencies = [ name = "session-store" version = "0.1.0" dependencies = [ + "agen", "async-trait", "base64 0.22.1", "futures", - "llm-engine", "protocol", "serde", "serde_json", @@ -4408,10 +4408,10 @@ dependencies = [ name = "ticket" version = "0.1.0" dependencies = [ + "agen", "async-trait", "chrono", "fs4", - "llm-engine", "project-record", "rusqlite", "schemars", @@ -4612,11 +4612,11 @@ checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" name = "tools" version = "0.1.0" dependencies = [ + "agen", "async-trait", "filetime", "fs-operation", "html5ever", - "llm-engine", "manifest", "markup5ever_rcdom", "pdf-extract", @@ -4794,11 +4794,11 @@ checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" name = "tui" version = "0.1.0" dependencies = [ + "agen", "base64 0.22.1", "client", "crossterm 0.28.1", "fs4", - "llm-engine", "manifest", "protocol", "pulldown-cmark", @@ -6060,6 +6060,7 @@ dependencies = [ name = "worker" version = "0.1.0" dependencies = [ + "agen", "arc-swap", "async-trait", "chrono", @@ -6074,7 +6075,6 @@ dependencies = [ "futures-util", "include_dir", "libc", - "llm-engine", "manifest", "mcp", "memory", @@ -6108,13 +6108,13 @@ dependencies = [ name = "worker-runtime" version = "0.1.0" dependencies = [ + "agen", "async-trait", "axum", "base64 0.22.1", "decodal", "flow", "futures", - "llm-engine", "manifest", "protocol", "reqwest", diff --git a/Cargo.toml b/Cargo.toml index 1fec7f77..044f958b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,8 +2,8 @@ resolver = "2" members = [ "crates/client", - "crates/llm-engine", - "crates/llm-engine-macros", + "crates/agen", + "crates/agen-macros", "crates/session-store", "crates/secrets", "crates/manifest", @@ -33,8 +33,8 @@ members = [ ] default-members = [ "crates/client", - "crates/llm-engine", - "crates/llm-engine-macros", + "crates/agen", + "crates/agen-macros", "crates/session-store", "crates/secrets", "crates/manifest", @@ -69,8 +69,8 @@ license = "MIT" [workspace.dependencies] # Internal crates client = { path = "crates/client" } -llm-engine = { path = "crates/llm-engine", version = "0.2" } -llm-engine-macros = { path = "crates/llm-engine-macros", version = "0.2" } +agen = { path = "crates/agen", version = "0.2" } +agen-macros = { path = "crates/agen-macros", version = "0.2" } manifest = { path = "crates/manifest" } mcp = { path = "crates/mcp" } lint-common = { path = "crates/lint-common" } diff --git a/crates/agen-macros/Cargo.toml b/crates/agen-macros/Cargo.toml new file mode 100644 index 00000000..604f4d96 --- /dev/null +++ b/crates/agen-macros/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "agen-macros" +description = "Procedural macros for declaring agen tools" +version = "0.2.0" +edition.workspace = true +rust-version = "1.85" +license.workspace = true +readme = "README.md" +repository = "https://gitea.hareworks.net/Hare/yoi" +homepage = "https://gitea.hareworks.net/Hare/yoi" +documentation = "https://docs.rs/agen-macros" +keywords = ["llm", "agent", "tools", "macros"] +categories = ["development-tools::procedural-macro-helpers"] +include = ["src/**", "README.md", "LICENSE"] + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = "1" +quote = "1" +syn = { version = "2", features = ["full"] } + +[package.metadata.docs.rs] +all-features = true diff --git a/crates/agen-macros/LICENSE b/crates/agen-macros/LICENSE new file mode 100644 index 00000000..5caa73c9 --- /dev/null +++ b/crates/agen-macros/LICENSE @@ -0,0 +1,7 @@ +Copyright 2026 Hare + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/crates/agen-macros/README.md b/crates/agen-macros/README.md new file mode 100644 index 00000000..679cc1ab --- /dev/null +++ b/crates/agen-macros/README.md @@ -0,0 +1,32 @@ +# agen-macros + +Procedural macros used by [`agen`](https://crates.io/crates/agen) to declare LLM tools from Rust methods. + +Applications should normally depend only on `agen` and import its re-exports: + +```rust +use agen::tool_registry; + +#[derive(Clone)] +struct Tools; + +#[tool_registry] +impl Tools { + /// Returns the supplied text. + #[tool] + async fn echo( + &self, + #[description = "Text to return"] text: String, + ) -> Result { + Ok(text) + } +} +``` + +`#[tool_registry]` generates the argument schema, a `Tool` implementation, and an `_definition` constructor. It rejects arguments of its own, duplicate `#[tool]` markers, malformed or duplicate `#[description = "..."]` attributes, and non-identifier argument patterns. + +Generated code targets the canonical `::agen` path and uses implementation dependencies re-exported by `agen`; consumers do not need direct `serde`, `schemars`, `serde_json`, or `async-trait` dependencies. Renaming the `agen` dependency in `Cargo.toml` is not currently supported. + +This companion package is published before the matching `agen` release. Its public contract is the generated API consumed by `agen`, and its minor version compatibility follows the `agen` 0.2 series. + +Licensed under the [MIT License](https://gitea.hareworks.net/Hare/yoi/src/branch/develop/LICENSE). diff --git a/crates/agen-macros/src/lib.rs b/crates/agen-macros/src/lib.rs new file mode 100644 index 00000000..e1ef6f96 --- /dev/null +++ b/crates/agen-macros/src/lib.rs @@ -0,0 +1,482 @@ +//! Procedural macros for declaring [`agen`](https://docs.rs/agen) tools. +//! +//! [`tool_registry`] expands methods marked with `#[tool]` into `agen::tool::Tool` +//! implementations and tool definitions. Applications normally use the re-exports from +//! `agen`; this companion crate exists so those macros can be published and versioned +//! independently. + +use proc_macro::TokenStream; +use quote::{format_ident, quote}; +use syn::{ + Attribute, FnArg, ImplItem, ItemImpl, Lit, Meta, Pat, ReturnType, Type, parse_macro_input, + spanned::Spanned, +}; + +/// Generates tools for methods marked with `#[tool]` in an `impl` block. +/// +/// Method doc comments become the tool description. An argument can use +/// `#[description = "..."]` to supply its JSON Schema description. +/// +/// ```ignore +/// #[derive(Clone)] +/// struct MyApp; +/// +/// #[agen::tool_registry] +/// impl MyApp { +/// /// Retrieves a user by ID. +/// #[tool] +/// async fn get_user( +/// &self, +/// #[description = "The user ID"] user_id: String, +/// ) -> Result { +/// todo!() +/// } +/// } +/// ``` +/// +/// This generates a `ToolGetUser` wrapper, a `GetUserArgs` schema type, and +/// `MyApp::get_user_definition()`. +#[proc_macro_attribute] +pub fn tool_registry(attr: TokenStream, item: TokenStream) -> TokenStream { + let attr = proc_macro2::TokenStream::from(attr); + let impl_block = parse_macro_input!(item as ItemImpl); + + expand_tool_registry(attr, impl_block) + .unwrap_or_else(syn::Error::into_compile_error) + .into() +} + +fn expand_tool_registry( + attr: proc_macro2::TokenStream, + mut impl_block: ItemImpl, +) -> syn::Result { + if !attr.is_empty() { + return Err(syn::Error::new( + attr.span(), + "tool_registry does not accept arguments", + )); + } + + let self_ty = impl_block.self_ty.as_ref().clone(); + let mut generated_items = Vec::new(); + + for item in &mut impl_block.items { + let ImplItem::Fn(method) = item else { + continue; + }; + + let tool_attrs: Vec<_> = method + .attrs + .iter() + .filter(|attr| attr.path().is_ident("tool")) + .collect(); + if tool_attrs.len() > 1 { + return Err(syn::Error::new_spanned( + tool_attrs[1], + "duplicate #[tool] attribute", + )); + } + let Some(tool_attr) = tool_attrs.first() else { + continue; + }; + if !matches!(tool_attr.meta, Meta::Path(_)) { + return Err(syn::Error::new_spanned( + tool_attr, + "#[tool] does not accept arguments", + )); + } + + method.attrs.retain(|attr| !attr.path().is_ident("tool")); + generated_items.push(generate_tool_impl(&self_ty, method)?); + + for input in &mut method.sig.inputs { + if let FnArg::Typed(pat_type) = input { + pat_type + .attrs + .retain(|attr| !attr.path().is_ident("description")); + } + } + } + + Ok(quote! { + #impl_block + + #(#generated_items)* + }) +} + +fn extract_doc_comment(attrs: &[Attribute]) -> String { + let mut lines = Vec::new(); + + for attr in attrs { + if attr.path().is_ident("doc") + && let Meta::NameValue(meta) = &attr.meta + && let syn::Expr::Lit(expr_lit) = &meta.value + && let Lit::Str(lit_str) = &expr_lit.lit + { + let line = lit_str.value(); + let trimmed = line.strip_prefix(' ').unwrap_or(&line); + lines.push(trimmed.to_string()); + } + } + + lines.join("\n") +} + +fn extract_description_attr(attrs: &[Attribute]) -> syn::Result> { + let mut description = None; + + for attr in attrs + .iter() + .filter(|attr| attr.path().is_ident("description")) + { + let value = match &attr.meta { + Meta::NameValue(meta) => match &meta.value { + syn::Expr::Lit(expr_lit) => match &expr_lit.lit { + Lit::Str(value) => value.value(), + _ => { + return Err(syn::Error::new_spanned( + attr, + "description must be a string literal", + )); + } + }, + _ => { + return Err(syn::Error::new_spanned( + attr, + "description must be a string literal", + )); + } + }, + _ => { + return Err(syn::Error::new_spanned( + attr, + "expected #[description = \"...\"]", + )); + } + }; + + if description.replace(value).is_some() { + return Err(syn::Error::new_spanned( + attr, + "duplicate #[description] attribute", + )); + } + } + + Ok(description) +} + +fn argument_ident(pat: &Pat) -> syn::Result<&syn::Ident> { + match pat { + Pat::Ident(pat_ident) => Ok(&pat_ident.ident), + _ => Err(syn::Error::new_spanned( + pat, + "tool arguments must use simple identifier patterns", + )), + } +} + +fn is_tool_execution_context_type(ty: &Type) -> bool { + let Type::Path(path) = ty else { + return false; + }; + path.path + .segments + .last() + .is_some_and(|segment| segment.ident == "ToolExecutionContext") +} + +fn generate_tool_impl( + self_ty: &Type, + method: &syn::ImplItemFn, +) -> syn::Result { + let sig = &method.sig; + let method_name = &sig.ident; + let tool_name = method_name.to_string(); + + let pascal_name = to_pascal_case(&method_name.to_string()); + let tool_struct_name = format_ident!("Tool{}", pascal_name); + let args_struct_name = format_ident!("{}Args", pascal_name); + let definition_name = format_ident!("{}_definition", method_name); + + let description = extract_doc_comment(&method.attrs); + let description = if description.is_empty() { + format!("Tool: {}", tool_name) + } else { + description + }; + + let method_args: Vec<_> = sig + .inputs + .iter() + .filter_map(|arg| match arg { + FnArg::Typed(pat_type) => Some(pat_type), + FnArg::Receiver(_) => None, + }) + .collect(); + let json_args: Vec<_> = method_args + .iter() + .copied() + .filter(|pat_type| !is_tool_execution_context_type(pat_type.ty.as_ref())) + .collect(); + + let arg_fields: Vec<_> = json_args + .iter() + .map(|pat_type| { + let field_name = argument_ident(pat_type.pat.as_ref())?; + let ty = &pat_type.ty; + let description = extract_description_attr(&pat_type.attrs)?; + + Ok(if let Some(description) = description { + quote! { + #[schemars(description = #description)] + pub #field_name: #ty + } + } else { + quote! { + pub #field_name: #ty + } + }) + }) + .collect::>()?; + + let call_args: Vec<_> = method_args + .iter() + .map(|pat_type| { + if is_tool_execution_context_type(pat_type.ty.as_ref()) { + Ok(quote! { ctx.clone() }) + } else { + let ident = argument_ident(pat_type.pat.as_ref())?; + Ok(quote! { args.#ident }) + } + }) + .collect::>()?; + let method_call = if call_args.is_empty() { + quote! { self.ctx.#method_name() } + } else { + quote! { self.ctx.#method_name(#(#call_args),*) } + }; + + let awaiter = if sig.asyncness.is_some() { + quote! { .await } + } else { + quote! {} + }; + + let result_handling = if is_result_type(&sig.output) { + quote! { + match result { + Ok(val) => Ok(format!("{:?}", val).into()), + Err(error) => Err(::agen::tool::ToolError::ExecutionFailed(format!("{}", error))), + } + } + } else { + quote! { + Ok(format!("{:?}", result).into()) + } + }; + + let args_struct_def = quote! { + #[derive( + ::agen::__private::serde::Deserialize, + ::agen::__private::schemars::JsonSchema, + )] + #[serde(crate = "::agen::__private::serde")] + #[schemars(crate = "::agen::__private::schemars")] + struct #args_struct_name { + #(#arg_fields),* + } + }; + + let execute_body = if json_args.is_empty() { + quote! { + let _: #args_struct_name = ::agen::__private::serde_json::from_str(input_json) + .unwrap_or(#args_struct_name {}); + + let result = #method_call #awaiter; + #result_handling + } + } else { + quote! { + let args: #args_struct_name = ::agen::__private::serde_json::from_str(input_json) + .map_err(|error| ::agen::tool::ToolError::InvalidArgument(error.to_string()))?; + + let result = #method_call #awaiter; + #result_handling + } + }; + + Ok(quote! { + #args_struct_def + + #[derive(Clone)] + pub struct #tool_struct_name { + ctx: #self_ty, + } + + #[::agen::__private::async_trait::async_trait] + impl ::agen::tool::Tool for #tool_struct_name { + async fn execute( + &self, + input_json: &str, + ctx: ::agen::tool::ToolExecutionContext, + ) -> Result<::agen::tool::ToolOutput, ::agen::tool::ToolError> { + let _ = &ctx; + #execute_body + } + } + + impl #self_ty { + /// Returns a tool definition for registration with an `agen::Engine`. + pub fn #definition_name(&self) -> ::agen::tool::ToolDefinition { + let ctx = self.clone(); + ::std::sync::Arc::new(move || { + let schema = ::agen::__private::schemars::schema_for!(#args_struct_name); + let meta = ::agen::tool::ToolMeta::new(#tool_name) + .description(#description) + .input_schema( + ::agen::__private::serde_json::to_value(schema) + .unwrap_or_else(|_| ::agen::__private::serde_json::json!({})), + ); + let tool: ::std::sync::Arc = + ::std::sync::Arc::new(#tool_struct_name { ctx: ctx.clone() }); + (meta, tool) + }) + } + } + }) +} + +fn is_result_type(return_type: &ReturnType) -> bool { + match return_type { + ReturnType::Default => false, + ReturnType::Type(_, ty) => { + if let Type::Path(type_path) = ty.as_ref() + && let Some(segment) = type_path.path.segments.last() + { + return segment.ident == "Result"; + } + false + } + } +} + +fn to_pascal_case(s: &str) -> String { + s.split('_') + .map(|part| { + let mut chars = part.chars(); + match chars.next() { + None => String::new(), + Some(first) => first.to_uppercase().chain(chars).collect(), + } + }) + .collect() +} + +/// Marker attribute interpreted by [`tool_registry`]. +#[proc_macro_attribute] +pub fn tool(attr: TokenStream, item: TokenStream) -> TokenStream { + marker_attribute("tool", attr, item) +} + +/// Argument description marker interpreted by [`tool_registry`]. +/// +/// Use it as `#[description = "The argument description"]` on a tool method argument. +#[proc_macro_attribute] +pub fn description(attr: TokenStream, item: TokenStream) -> TokenStream { + marker_attribute("description", attr, item) +} + +fn marker_attribute(name: &str, attr: TokenStream, item: TokenStream) -> TokenStream { + if attr.is_empty() { + item + } else { + syn::Error::new( + proc_macro2::Span::call_site(), + format!("{name} is a marker interpreted by #[tool_registry]"), + ) + .into_compile_error() + .into() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use quote::quote; + use syn::parse_quote; + + #[test] + fn rejects_tool_registry_arguments() { + let implementation: ItemImpl = parse_quote!(impl Registry {}); + let error = expand_tool_registry(quote!(unexpected), implementation).unwrap_err(); + + assert!(error.to_string().contains("does not accept arguments")); + } + + #[test] + fn rejects_duplicate_tool_markers() { + let implementation: ItemImpl = parse_quote! { + impl Registry { + #[tool] + #[tool] + fn inspect(&self) {} + } + }; + let error = expand_tool_registry(quote!(), implementation).unwrap_err(); + + assert!(error.to_string().contains("duplicate #[tool]")); + } + + #[test] + fn rejects_invalid_description_attributes() { + let implementation: ItemImpl = parse_quote! { + impl Registry { + #[tool] + fn inspect(&self, #[description] input: String) {} + } + }; + let error = expand_tool_registry(quote!(), implementation).unwrap_err(); + + assert!(error.to_string().contains("expected #[description")); + } + + #[test] + fn rejects_duplicate_description_attributes() { + let implementation: ItemImpl = parse_quote! { + impl Registry { + #[tool] + fn inspect( + &self, + #[description = "first"] + #[description = "second"] + input: String, + ) {} + } + }; + let error = expand_tool_registry(quote!(), implementation).unwrap_err(); + + assert!(error.to_string().contains("duplicate #[description]")); + } + + #[test] + fn generated_code_uses_only_agen_runtime_paths() { + let implementation: ItemImpl = parse_quote! { + impl Registry { + #[tool] + fn inspect(&self, input: String) -> Result { + unreachable!() + } + } + }; + let expanded = expand_tool_registry(quote!(), implementation) + .unwrap() + .to_string(); + + assert!(expanded.contains(":: agen :: tool :: Tool")); + assert!(expanded.contains(":: agen :: __private :: serde_json")); + assert!(expanded.contains(":: agen :: __private :: serde")); + assert!(expanded.contains(":: agen :: __private :: schemars")); + } +} diff --git a/crates/llm-engine/Cargo.toml b/crates/agen/Cargo.toml similarity index 58% rename from crates/llm-engine/Cargo.toml rename to crates/agen/Cargo.toml index 85d0a837..febf6df1 100644 --- a/crates/llm-engine/Cargo.toml +++ b/crates/agen/Cargo.toml @@ -1,9 +1,18 @@ [package] -name = "llm-engine" -description = "A library for building autonomous LLM-powered systems" +name = "agen" +description = "Provider-neutral orchestration for tool-using LLM applications" version = "0.2.1" edition.workspace = true +rust-version = "1.86" license.workspace = true +readme = "README.md" +repository = "https://gitea.hareworks.net/Hare/yoi" +homepage = "https://gitea.hareworks.net/Hare/yoi" +documentation = "https://docs.rs/agen" +keywords = ["llm", "agent", "tools", "streaming", "orchestration"] +categories = ["api-bindings", "asynchronous"] +include = ["src/**", "tests/**", "examples/*.rs", "docs/**", "README.md", "LICENSE"] +autoexamples = false [features] default = [] @@ -12,6 +21,7 @@ codex = ["dep:chrono"] [dependencies] serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } +schemars = { workspace = true } thiserror = { workspace = true } tracing = { workspace = true } async-trait = { workspace = true } @@ -23,13 +33,23 @@ eventsource-stream = "0.2" zstd = "0.13" base64 = "0.22.1" chrono = { version = "0.4", default-features = false, features = ["serde", "clock"], optional = true } -llm-engine-macros = { workspace = true } +agen-macros = { workspace = true } [dev-dependencies] clap = { version = "4.5", features = ["derive", "env"] } -schemars = { workspace = true } tempfile = { workspace = true } dotenv = "0.15" tracing-subscriber = { version = "0.3", features = ["env-filter"] } trybuild = "1.0.116" wiremock = "0.6.5" + +[[example]] +name = "engine_cancel_demo" +path = "examples/engine_cancel_demo.rs" + +[[example]] +name = "engine_cli" +path = "examples/engine_cli.rs" + +[package.metadata.docs.rs] +all-features = true diff --git a/crates/agen/LICENSE b/crates/agen/LICENSE new file mode 100644 index 00000000..5caa73c9 --- /dev/null +++ b/crates/agen/LICENSE @@ -0,0 +1,7 @@ +Copyright 2026 Hare + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/crates/agen/README.md b/crates/agen/README.md new file mode 100644 index 00000000..1a80dacd --- /dev/null +++ b/crates/agen/README.md @@ -0,0 +1,90 @@ +# agen + +`agen` is a provider-neutral Rust engine for streaming LLM applications that use tools. It owns the turn loop, typed conversation history, provider wire-format adapters, tool execution, interceptors, usage accounting, and cache-aware state transitions. + +> `agen` is pre-1.0. Public APIs may change between minor releases. + +## Installation + +```toml +[dependencies] +agen = "0.2.1" +``` + +The default feature set is intentionally empty. Enable the experimental Codex/ChatGPT authentication adapter when needed: + +```toml +agen = { version = "0.2.1", features = ["codex"] } +``` + +`agen` requires Rust 1.86 or newer. The companion `agen-macros` package requires Rust 1.85 or newer. + +## Quick start + +Supply an implementation of [`LlmClient`](https://docs.rs/agen/latest/agen/llm_client/trait.LlmClient.html), then run a turn. The first call consumes the mutable engine and returns a cache-locked engine for later turns. + +```no_run +use agen::{Engine, EngineError}; +use agen::llm_client::LlmClient; + +async fn conversation(client: C) -> Result<(), EngineError> { + let output = Engine::new(client) + .system_prompt("You are a concise assistant.") + .run("Explain typed state in one sentence.") + .await?; + + let mut engine = output.engine; + let _result = engine.run("Give a Rust example.").await?; + Ok(()) +} +``` + +## Declaring tools + +The tool macros are re-exported by `agen`; applications do not need direct dependencies on `serde`, `schemars`, `serde_json`, or `async-trait` for generated code. + +```rust +use agen::tool_registry; + +#[derive(Clone)] +struct Tools; + +#[tool_registry] +impl Tools { + /// Returns the supplied text. + #[tool] + async fn echo( + &self, + #[description = "Text to return"] text: String, + ) -> Result { + Ok(text) + } +} + +let definition = Tools.echo_definition(); +assert_eq!(definition().0.name, "echo"); +``` + +The generated API uses the canonical crate name `agen`. Renaming the `agen` dependency in `Cargo.toml` is not currently supported by these macros. + +## Features + +| Feature | Default | Adds | +|---|---:|---| +| `codex` | No | Experimental Codex/ChatGPT auth-file loading and token refresh support | + +The base crate includes provider-neutral transport and Anthropic, OpenAI-compatible, Gemini, and Ollama wire-format schemes. See [`llm_client`](https://docs.rs/agen/latest/agen/llm_client/) for the client boundary. + +## Architecture and API scope + +The current public modules cover the engine, typed history, client transport/schemes, timeline events, tools, interceptors, pruning, token estimation, and usage records. Their relationships are described in [Architecture](https://gitea.hareworks.net/Hare/yoi/src/branch/develop/crates/agen/docs/architecture.md); behavioral requirements are summarized in [Requirements](https://gitea.hareworks.net/Hare/yoi/src/branch/develop/crates/agen/docs/requirements.md). + +Low-level modules remain public in the 0.2 series because downstream Yoi components implement custom clients, event handlers, pruning policies, and tool registries against them. This surface is versioned as pre-1.0 API rather than declared stable. + +## Packaging and security + +The published package contains source, public documentation, curated examples, and deterministic tests/fixtures. Credentialed fixture-recording utilities are intentionally excluded. Examples that contact a provider read credentials from environment variables and never embed production credentials. + +## License + +Licensed under the [MIT License](https://gitea.hareworks.net/Hare/yoi/src/branch/develop/LICENSE). diff --git a/crates/agen/docs/architecture.md b/crates/agen/docs/architecture.md new file mode 100644 index 00000000..1209bd48 --- /dev/null +++ b/crates/agen/docs/architecture.md @@ -0,0 +1,62 @@ +# agen architecture + +`agen` separates orchestration, event projection, and provider transport so applications can replace an LLM client without changing the turn loop or tool model. + +```text +┌────────────────────────────────────────────┐ +│ Engine │ +│ turn loop · interceptors · tool execution │ +│ typed state: Mutable → Locked → Mutable │ +└─────────────────────┬──────────────────────┘ + │ +┌─────────────────────▼──────────────────────┐ +│ Timeline │ +│ event dispatch · block collectors │ +└─────────────────────┬──────────────────────┘ + │ +┌─────────────────────▼──────────────────────┐ +│ LlmClient │ +│ transport · provider wire-format schemes │ +└────────────────────────────────────────────┘ +``` + +## Main modules + +| Module | Responsibility | +|---|---| +| `engine` | Turn execution, pause/resume, retries, tool integration, and callbacks | +| `state` | Sealed `Mutable` and `Locked` type-state markers | +| `interceptor` | Application-owned control decisions at orchestration boundaries | +| `tool` / `tool_server` | Tool metadata, registration, execution, and bounded output | +| `timeline` | Streaming event dispatch, handlers, and block assembly | +| `llm_client` | Provider-neutral request, response, auth, transport, and scheme contracts | +| `providers` | Optional higher-level provider adapters such as the `codex` feature | +| `prune` / `token_counter` | Cache-aware history reduction and token estimation | +| `usage_record` | Request and token usage accounting | + +## Request flow + +```text +Engine history + → provider-neutral Request + → Scheme::build_request + → Provider transport +``` + +## Response flow + +```text +streaming response bytes + → Scheme event parsing + → unified Event values + → Timeline handlers and collectors + → Engine history/tool decisions +``` + +## Type state and cache protection + +`Engine` permits configuration and history editing. `Engine::run` or `Engine::lock` commits the current prefix and produces `Engine`. The locked engine may append turns without mutating the committed prefix. `Engine::unlock` explicitly returns to mutable state when an application accepts losing that cache guarantee. + +## Public surface + +The 0.2 series exposes the low-level client, timeline, tool, pruning, and usage modules because custom clients and orchestration hosts build directly on them. These APIs are intentionally provider-neutral but remain pre-1.0 and may change in later minor releases. diff --git a/crates/agen/docs/requirements.md b/crates/agen/docs/requirements.md new file mode 100644 index 00000000..d564b950 --- /dev/null +++ b/crates/agen/docs/requirements.md @@ -0,0 +1,39 @@ +# agen requirements + +## R1: Turn execution and continuation + +- `Engine::run` starts a turn and loops through provider output and tool calls. +- An `Interceptor` may continue, cancel, or pause work at defined orchestration boundaries. +- `Engine::resume` continues paused generation without fabricating another user message. +- Cancellation and provider errors are represented as typed `EngineError` values. + +## R2: Explicit cache-preserving state + +- `Engine` permits configuration and history edits. +- `Engine::run` or `Engine::lock` transitions to `Engine` and records the committed prefix. +- A locked engine appends turns but cannot mutate that prefix through mutable-only APIs. +- `Engine::unlock` explicitly abandons the lock before configuration or history changes. + +## R3: Tool declarations and execution + +- `#[tool_registry]` generates a schema and `Tool` implementation for methods marked `#[tool]`. +- `#[description = "..."]` supplies argument descriptions in generated JSON Schema. +- Generated code resolves its runtime and helper dependencies through `::agen`. +- Invalid and duplicate marker attributes produce compile errors rather than panics. +- Tools execute through `ToolServer` with typed context, errors, and output limits. + +## R4: Provider-neutral orchestration + +- `LlmClient` is the boundary between the engine and provider-specific transport. +- Request/response schemes translate provider wire formats into shared request and event types. +- Interceptors, tool execution, timeline collection, and pruning stay above the provider transport. +- Provider-specific capabilities are optional features when they require additional policy or dependencies. + +## R5: Publication quality + +- crates.io metadata includes license, repository, documentation, README, categories, keywords, and MSRV. +- The default feature set and each optional feature compile and test independently. +- Macro expansion compiles in a downstream-style integration test without direct helper dependencies. +- rustdoc builds without dependency documentation. +- Package contents are explicitly bounded and exclude credentialed fixture-recording utilities. +- `cargo package` and `cargo publish --dry-run` are run for `agen-macros` before `agen` because the main package depends on its companion package. diff --git a/crates/llm-engine/examples/engine_cancel_demo.rs b/crates/agen/examples/engine_cancel_demo.rs similarity index 91% rename from crates/llm-engine/examples/engine_cancel_demo.rs rename to crates/agen/examples/engine_cancel_demo.rs index 6ec75c5a..6c7cbe13 100644 --- a/crates/llm-engine/examples/engine_cancel_demo.rs +++ b/crates/agen/examples/engine_cancel_demo.rs @@ -2,9 +2,9 @@ //! //! Example of cancelling from another thread during streaming -use llm_engine::llm_client::scheme::{Scheme, anthropic::AnthropicScheme}; -use llm_engine::llm_client::transport::{HttpTransport, ResolvedAuth}; -use llm_engine::{Engine, EngineResult}; +use agen::llm_client::scheme::{Scheme, anthropic::AnthropicScheme}; +use agen::llm_client::transport::{HttpTransport, ResolvedAuth}; +use agen::{Engine, EngineResult}; use std::time::Duration; #[tokio::main] diff --git a/crates/llm-engine/examples/engine_cli.rs b/crates/agen/examples/engine_cli.rs similarity index 99% rename from crates/llm-engine/examples/engine_cli.rs rename to crates/agen/examples/engine_cli.rs index 8facc5f1..f0ebff9d 100644 --- a/crates/llm-engine/examples/engine_cli.rs +++ b/crates/agen/examples/engine_cli.rs @@ -38,8 +38,7 @@ use async_trait::async_trait; use tracing::info; use tracing_subscriber::EnvFilter; -use clap::{Parser, ValueEnum}; -use llm_engine::{ +use agen::{ Engine, interceptor::{Interceptor, PostToolAction, ToolResultInfo}, llm_client::{ @@ -51,12 +50,9 @@ use llm_engine::{ transport::{HttpTransport, ResolvedAuth}, }, timeline::{Handler, TextBlockEvent, TextBlockKind, ToolUseBlockEvent, ToolUseBlockKind}, + tool_registry, }; -use llm_engine_macros::tool_registry; - -// Required imports for macro expansion -use schemars; -use serde; +use clap::{Parser, ValueEnum}; // ============================================================================= // Provider Definition diff --git a/crates/llm-engine/examples/record_test_fixtures/main.rs b/crates/agen/examples/record_test_fixtures/main.rs similarity index 98% rename from crates/llm-engine/examples/record_test_fixtures/main.rs rename to crates/agen/examples/record_test_fixtures/main.rs index 485e6669..f1978efa 100644 --- a/crates/llm-engine/examples/record_test_fixtures/main.rs +++ b/crates/agen/examples/record_test_fixtures/main.rs @@ -19,11 +19,11 @@ mod recorder; mod scenarios; -use clap::{Parser, ValueEnum}; -use llm_engine::llm_client::scheme::{ +use agen::llm_client::scheme::{ Scheme, anthropic::AnthropicScheme, gemini::GeminiScheme, openai_chat::OpenAIScheme, }; -use llm_engine::llm_client::transport::{HttpTransport, ResolvedAuth}; +use agen::llm_client::transport::{HttpTransport, ResolvedAuth}; +use clap::{Parser, ValueEnum}; fn make_transport(scheme: S, model: &str, auth: ResolvedAuth) -> HttpTransport { let cap = scheme.default_capability(); diff --git a/crates/llm-engine/examples/record_test_fixtures/recorder.rs b/crates/agen/examples/record_test_fixtures/recorder.rs similarity index 98% rename from crates/llm-engine/examples/record_test_fixtures/recorder.rs rename to crates/agen/examples/record_test_fixtures/recorder.rs index 3a422ec2..71a7c46a 100644 --- a/crates/llm-engine/examples/record_test_fixtures/recorder.rs +++ b/crates/agen/examples/record_test_fixtures/recorder.rs @@ -7,8 +7,8 @@ use std::io::{BufWriter, Write}; use std::path::Path; use std::time::{Instant, SystemTime, UNIX_EPOCH}; +use agen::llm_client::{LlmClient, Request}; use futures::StreamExt; -use llm_engine::llm_client::{LlmClient, Request}; /// Recorded event #[derive(Debug, serde::Serialize, serde::Deserialize)] diff --git a/crates/llm-engine/examples/record_test_fixtures/scenarios.rs b/crates/agen/examples/record_test_fixtures/scenarios.rs similarity index 97% rename from crates/llm-engine/examples/record_test_fixtures/scenarios.rs rename to crates/agen/examples/record_test_fixtures/scenarios.rs index 62845bea..00d7ee59 100644 --- a/crates/llm-engine/examples/record_test_fixtures/scenarios.rs +++ b/crates/agen/examples/record_test_fixtures/scenarios.rs @@ -2,7 +2,7 @@ //! //! Defines requests and output file names for each scenario -use llm_engine::llm_client::{Request, ToolDefinition}; +use agen::llm_client::{Request, ToolDefinition}; /// Test scenario pub struct TestScenario { diff --git a/crates/llm-engine/src/callback.rs b/crates/agen/src/callback.rs similarity index 100% rename from crates/llm-engine/src/callback.rs rename to crates/agen/src/callback.rs diff --git a/crates/llm-engine/src/engine.rs b/crates/agen/src/engine.rs similarity index 99% rename from crates/llm-engine/src/engine.rs rename to crates/agen/src/engine.rs index 942a4fe1..15c22d82 100644 --- a/crates/llm-engine/src/engine.rs +++ b/crates/agen/src/engine.rs @@ -88,7 +88,7 @@ pub enum EngineResult { Yielded, } -/// Result of [`Engine::run()`] / [`Engine::resume()`]. +/// Result of [`Engine::run`] or [`Engine::resume`]. /// /// Contains the `Locked` Engine (ready for subsequent runs) and the outcome. pub struct EngineRunOutput { @@ -181,7 +181,7 @@ pub struct Engine { locked_prefix_len: usize, /// AgentTurn count. /// - /// Once retry (`llm-engine-stream-continuation`) is implemented, an + /// Once retry (`agen-stream-continuation`) is implemented, an /// AgentTurn collapses N retried `LlmCall`s with identical input; /// today retry is not implemented so AgentTurn and LlmCall fire 1:1 /// and the increment site (the LLM-call loop) is shared. @@ -1242,7 +1242,7 @@ impl Engine { } // LlmCall boundary fires per LLM generation request — today - // 1:1 with AgentTurn, but retry (`llm-engine-stream-continuation`) + // 1:1 with AgentTurn, but retry (`agen-stream-continuation`) // will multiply this within a single AgentTurn. let current_llm_call = self.llm_call_count; for cb in &self.llm_call_start_cbs { @@ -1696,8 +1696,7 @@ impl Engine { /// Register a tool factory for deferred initialization. /// /// The factory is queued and executed at the next `run()` or `resume()` call. - /// Duplicate name detection occurs at that point and surfaces as - /// [`EngineError::ToolRegistry`]. + /// Duplicate name detection occurs when pending tools are flushed before that call. pub fn register_tool(&mut self, factory: EngineToolDefinition) { self.tool_server.register_tool(factory); } @@ -1880,7 +1879,7 @@ impl Engine { /// This is the primary entry point for first use. Equivalent to /// `self.lock()` followed by `locked.run(user_input)`. /// - /// Subsequent runs can use [`Engine::run()`] directly. + /// Subsequent runs can call [`Engine::run`] directly. /// To edit state between turns, call [`unlock()`](Engine::unlock) first. pub async fn run( self, diff --git a/crates/llm-engine/src/event.rs b/crates/agen/src/event.rs similarity index 100% rename from crates/llm-engine/src/event.rs rename to crates/agen/src/event.rs diff --git a/crates/llm-engine/src/handler.rs b/crates/agen/src/handler.rs similarity index 98% rename from crates/llm-engine/src/handler.rs rename to crates/agen/src/handler.rs index 538a66f2..1be7060c 100644 --- a/crates/llm-engine/src/handler.rs +++ b/crates/agen/src/handler.rs @@ -32,7 +32,7 @@ pub trait Kind { /// # Examples /// /// ```ignore -/// use llm_engine::timeline::{Handler, TextBlockEvent, TextBlockKind}; +/// use agen::timeline::{Handler, TextBlockEvent, TextBlockKind}; /// /// struct TextCollector { /// texts: Vec, diff --git a/crates/llm-engine/src/interceptor.rs b/crates/agen/src/interceptor.rs similarity index 100% rename from crates/llm-engine/src/interceptor.rs rename to crates/agen/src/interceptor.rs diff --git a/crates/agen/src/lib.rs b/crates/agen/src/lib.rs new file mode 100644 index 00000000..29769286 --- /dev/null +++ b/crates/agen/src/lib.rs @@ -0,0 +1,42 @@ +#![doc = include_str!("../README.md")] + +mod engine; +mod handler; +mod message; + +pub(crate) mod callback; +pub mod event; +pub mod interceptor; +pub mod llm_client; +pub mod providers; +pub mod prune; +pub mod state; +pub mod timeline; +pub mod token_counter; +pub mod tool; +pub mod tool_server; +pub mod usage_record; + +pub use agen_macros::{description, tool, tool_registry}; +pub use callback::{TextBlockScope, ThinkingBlockScope, ToolUseBlockScope}; +pub use engine::{ + Engine, EngineConfig, EngineError, EngineResult, EngineRunOutput, LlmRetryNotice, + ToolRegistryError, +}; +pub use handler::ToolUseBlockStart; +pub use interceptor::Interceptor; +pub use message::{ContentPart, Item, Message, Role}; +pub use tool::{ToolCall, ToolExecutionContext, ToolOutputLimits, ToolResult}; +pub use usage_record::UsageRecord; + +/// Implementation dependencies used by code generated from `agen` macros. +/// +/// This module is not a stable user-facing API. It is public only because macro expansion +/// happens in the downstream crate. +#[doc(hidden)] +pub mod __private { + pub use async_trait; + pub use schemars; + pub use serde; + pub use serde_json; +} diff --git a/crates/llm-engine/src/llm_client/auth.rs b/crates/agen/src/llm_client/auth.rs similarity index 98% rename from crates/llm-engine/src/llm_client/auth.rs rename to crates/agen/src/llm_client/auth.rs index e35f4fff..08cc61a5 100644 --- a/crates/llm-engine/src/llm_client/auth.rs +++ b/crates/agen/src/llm_client/auth.rs @@ -30,7 +30,7 @@ pub enum AuthRequirement { /// リクエスト毎に認証ヘッダを動的に組み立てるプロバイダ。 /// /// access token が refresh で更新されたり、複数ヘッダを同時に注入する -/// 必要があるケースで使う。実体は呼び出し側に置き、llm-engine は +/// 必要があるケースで使う。実体は呼び出し側に置き、agen は /// trait を知るだけ。 /// /// 返したヘッダはそのまま `HeaderMap` に挿入される。`Authorization` diff --git a/crates/llm-engine/src/llm_client/capability.rs b/crates/agen/src/llm_client/capability.rs similarity index 100% rename from crates/llm-engine/src/llm_client/capability.rs rename to crates/agen/src/llm_client/capability.rs diff --git a/crates/llm-engine/src/llm_client/client.rs b/crates/agen/src/llm_client/client.rs similarity index 100% rename from crates/llm-engine/src/llm_client/client.rs rename to crates/agen/src/llm_client/client.rs diff --git a/crates/llm-engine/src/llm_client/error.rs b/crates/agen/src/llm_client/error.rs similarity index 100% rename from crates/llm-engine/src/llm_client/error.rs rename to crates/agen/src/llm_client/error.rs diff --git a/crates/llm-engine/src/llm_client/event.rs b/crates/agen/src/llm_client/event.rs similarity index 100% rename from crates/llm-engine/src/llm_client/event.rs rename to crates/agen/src/llm_client/event.rs diff --git a/crates/llm-engine/src/llm_client/mod.rs b/crates/agen/src/llm_client/mod.rs similarity index 100% rename from crates/llm-engine/src/llm_client/mod.rs rename to crates/agen/src/llm_client/mod.rs diff --git a/crates/llm-engine/src/llm_client/retry.rs b/crates/agen/src/llm_client/retry.rs similarity index 97% rename from crates/llm-engine/src/llm_client/retry.rs rename to crates/agen/src/llm_client/retry.rs index acaa88df..123084c5 100644 --- a/crates/llm-engine/src/llm_client/retry.rs +++ b/crates/agen/src/llm_client/retry.rs @@ -8,7 +8,7 @@ use std::time::Duration; /// 指数バックオフ + ジッター + 累積タイムアウトを表すポリシー。 /// -/// `Default` は llm-engine 全体の固定値を返す。呼び出し側からの上書きが +/// `Default` は agen 全体の固定値を返す。呼び出し側からの上書きが /// 必要になったら拡張する。 #[derive(Debug, Clone)] pub struct RetryPolicy { diff --git a/crates/llm-engine/src/llm_client/scheme/anthropic/capability.rs b/crates/agen/src/llm_client/scheme/anthropic/capability.rs similarity index 100% rename from crates/llm-engine/src/llm_client/scheme/anthropic/capability.rs rename to crates/agen/src/llm_client/scheme/anthropic/capability.rs diff --git a/crates/llm-engine/src/llm_client/scheme/anthropic/events.rs b/crates/agen/src/llm_client/scheme/anthropic/events.rs similarity index 100% rename from crates/llm-engine/src/llm_client/scheme/anthropic/events.rs rename to crates/agen/src/llm_client/scheme/anthropic/events.rs diff --git a/crates/llm-engine/src/llm_client/scheme/anthropic/mod.rs b/crates/agen/src/llm_client/scheme/anthropic/mod.rs similarity index 100% rename from crates/llm-engine/src/llm_client/scheme/anthropic/mod.rs rename to crates/agen/src/llm_client/scheme/anthropic/mod.rs diff --git a/crates/llm-engine/src/llm_client/scheme/anthropic/request.rs b/crates/agen/src/llm_client/scheme/anthropic/request.rs similarity index 100% rename from crates/llm-engine/src/llm_client/scheme/anthropic/request.rs rename to crates/agen/src/llm_client/scheme/anthropic/request.rs diff --git a/crates/llm-engine/src/llm_client/scheme/anthropic/scheme_impl.rs b/crates/agen/src/llm_client/scheme/anthropic/scheme_impl.rs similarity index 100% rename from crates/llm-engine/src/llm_client/scheme/anthropic/scheme_impl.rs rename to crates/agen/src/llm_client/scheme/anthropic/scheme_impl.rs diff --git a/crates/llm-engine/src/llm_client/scheme/gemini/capability.rs b/crates/agen/src/llm_client/scheme/gemini/capability.rs similarity index 100% rename from crates/llm-engine/src/llm_client/scheme/gemini/capability.rs rename to crates/agen/src/llm_client/scheme/gemini/capability.rs diff --git a/crates/llm-engine/src/llm_client/scheme/gemini/events.rs b/crates/agen/src/llm_client/scheme/gemini/events.rs similarity index 100% rename from crates/llm-engine/src/llm_client/scheme/gemini/events.rs rename to crates/agen/src/llm_client/scheme/gemini/events.rs diff --git a/crates/llm-engine/src/llm_client/scheme/gemini/mod.rs b/crates/agen/src/llm_client/scheme/gemini/mod.rs similarity index 100% rename from crates/llm-engine/src/llm_client/scheme/gemini/mod.rs rename to crates/agen/src/llm_client/scheme/gemini/mod.rs diff --git a/crates/llm-engine/src/llm_client/scheme/gemini/request.rs b/crates/agen/src/llm_client/scheme/gemini/request.rs similarity index 100% rename from crates/llm-engine/src/llm_client/scheme/gemini/request.rs rename to crates/agen/src/llm_client/scheme/gemini/request.rs diff --git a/crates/llm-engine/src/llm_client/scheme/gemini/scheme_impl.rs b/crates/agen/src/llm_client/scheme/gemini/scheme_impl.rs similarity index 100% rename from crates/llm-engine/src/llm_client/scheme/gemini/scheme_impl.rs rename to crates/agen/src/llm_client/scheme/gemini/scheme_impl.rs diff --git a/crates/llm-engine/src/llm_client/scheme/mod.rs b/crates/agen/src/llm_client/scheme/mod.rs similarity index 100% rename from crates/llm-engine/src/llm_client/scheme/mod.rs rename to crates/agen/src/llm_client/scheme/mod.rs diff --git a/crates/llm-engine/src/llm_client/scheme/openai_chat/capability.rs b/crates/agen/src/llm_client/scheme/openai_chat/capability.rs similarity index 100% rename from crates/llm-engine/src/llm_client/scheme/openai_chat/capability.rs rename to crates/agen/src/llm_client/scheme/openai_chat/capability.rs diff --git a/crates/llm-engine/src/llm_client/scheme/openai_chat/events.rs b/crates/agen/src/llm_client/scheme/openai_chat/events.rs similarity index 100% rename from crates/llm-engine/src/llm_client/scheme/openai_chat/events.rs rename to crates/agen/src/llm_client/scheme/openai_chat/events.rs diff --git a/crates/llm-engine/src/llm_client/scheme/openai_chat/mod.rs b/crates/agen/src/llm_client/scheme/openai_chat/mod.rs similarity index 100% rename from crates/llm-engine/src/llm_client/scheme/openai_chat/mod.rs rename to crates/agen/src/llm_client/scheme/openai_chat/mod.rs diff --git a/crates/llm-engine/src/llm_client/scheme/openai_chat/request.rs b/crates/agen/src/llm_client/scheme/openai_chat/request.rs similarity index 100% rename from crates/llm-engine/src/llm_client/scheme/openai_chat/request.rs rename to crates/agen/src/llm_client/scheme/openai_chat/request.rs diff --git a/crates/llm-engine/src/llm_client/scheme/openai_chat/scheme_impl.rs b/crates/agen/src/llm_client/scheme/openai_chat/scheme_impl.rs similarity index 100% rename from crates/llm-engine/src/llm_client/scheme/openai_chat/scheme_impl.rs rename to crates/agen/src/llm_client/scheme/openai_chat/scheme_impl.rs diff --git a/crates/llm-engine/src/llm_client/scheme/openai_responses/capability.rs b/crates/agen/src/llm_client/scheme/openai_responses/capability.rs similarity index 100% rename from crates/llm-engine/src/llm_client/scheme/openai_responses/capability.rs rename to crates/agen/src/llm_client/scheme/openai_responses/capability.rs diff --git a/crates/llm-engine/src/llm_client/scheme/openai_responses/events.rs b/crates/agen/src/llm_client/scheme/openai_responses/events.rs similarity index 100% rename from crates/llm-engine/src/llm_client/scheme/openai_responses/events.rs rename to crates/agen/src/llm_client/scheme/openai_responses/events.rs diff --git a/crates/llm-engine/src/llm_client/scheme/openai_responses/mod.rs b/crates/agen/src/llm_client/scheme/openai_responses/mod.rs similarity index 97% rename from crates/llm-engine/src/llm_client/scheme/openai_responses/mod.rs rename to crates/agen/src/llm_client/scheme/openai_responses/mod.rs index 76e287a8..b56d4ebd 100644 --- a/crates/llm-engine/src/llm_client/scheme/openai_responses/mod.rs +++ b/crates/agen/src/llm_client/scheme/openai_responses/mod.rs @@ -4,8 +4,8 @@ //! function_call item が first-class で、SSE イベントも `response.*` 名前空間で //! 流れる。 //! -//! - リクエスト JSON 生成: [`request`] -//! - SSE イベントパース → [`Event`](crate::llm_client::event::Event) 変換: [`events`] +//! - リクエスト JSON 生成: `request` +//! - SSE イベントパース → [`Event`](crate::llm_client::event::Event) 変換: `events` mod capability; mod events; diff --git a/crates/llm-engine/src/llm_client/scheme/openai_responses/request.rs b/crates/agen/src/llm_client/scheme/openai_responses/request.rs similarity index 100% rename from crates/llm-engine/src/llm_client/scheme/openai_responses/request.rs rename to crates/agen/src/llm_client/scheme/openai_responses/request.rs diff --git a/crates/llm-engine/src/llm_client/scheme/openai_responses/scheme_impl.rs b/crates/agen/src/llm_client/scheme/openai_responses/scheme_impl.rs similarity index 100% rename from crates/llm-engine/src/llm_client/scheme/openai_responses/scheme_impl.rs rename to crates/agen/src/llm_client/scheme/openai_responses/scheme_impl.rs diff --git a/crates/llm-engine/src/llm_client/transport.rs b/crates/agen/src/llm_client/transport.rs similarity index 100% rename from crates/llm-engine/src/llm_client/transport.rs rename to crates/agen/src/llm_client/transport.rs diff --git a/crates/llm-engine/src/llm_client/types.rs b/crates/agen/src/llm_client/types.rs similarity index 99% rename from crates/llm-engine/src/llm_client/types.rs rename to crates/agen/src/llm_client/types.rs index 77c61339..dd2d3dc9 100644 --- a/crates/llm-engine/src/llm_client/types.rs +++ b/crates/agen/src/llm_client/types.rs @@ -69,7 +69,7 @@ impl fmt::Debug for RequestTrace { /// # Examples /// /// ```ignore -/// use llm_engine::Item; +/// use agen::Item; /// /// let user = Item::user_message("Hello!"); /// let assistant = Item::assistant_message("Hi there!"); diff --git a/crates/llm-engine/src/message.rs b/crates/agen/src/message.rs similarity index 100% rename from crates/llm-engine/src/message.rs rename to crates/agen/src/message.rs diff --git a/crates/llm-engine/src/providers/codex/auth_json.rs b/crates/agen/src/providers/codex/auth_json.rs similarity index 100% rename from crates/llm-engine/src/providers/codex/auth_json.rs rename to crates/agen/src/providers/codex/auth_json.rs diff --git a/crates/llm-engine/src/providers/codex/error.rs b/crates/agen/src/providers/codex/error.rs similarity index 100% rename from crates/llm-engine/src/providers/codex/error.rs rename to crates/agen/src/providers/codex/error.rs diff --git a/crates/llm-engine/src/providers/codex/jwt.rs b/crates/agen/src/providers/codex/jwt.rs similarity index 100% rename from crates/llm-engine/src/providers/codex/jwt.rs rename to crates/agen/src/providers/codex/jwt.rs diff --git a/crates/llm-engine/src/providers/codex/mod.rs b/crates/agen/src/providers/codex/mod.rs similarity index 100% rename from crates/llm-engine/src/providers/codex/mod.rs rename to crates/agen/src/providers/codex/mod.rs diff --git a/crates/llm-engine/src/providers/codex/refresh.rs b/crates/agen/src/providers/codex/refresh.rs similarity index 100% rename from crates/llm-engine/src/providers/codex/refresh.rs rename to crates/agen/src/providers/codex/refresh.rs diff --git a/crates/llm-engine/src/providers/mod.rs b/crates/agen/src/providers/mod.rs similarity index 100% rename from crates/llm-engine/src/providers/mod.rs rename to crates/agen/src/providers/mod.rs diff --git a/crates/llm-engine/src/prune.rs b/crates/agen/src/prune.rs similarity index 100% rename from crates/llm-engine/src/prune.rs rename to crates/agen/src/prune.rs diff --git a/crates/llm-engine/src/state.rs b/crates/agen/src/state.rs similarity index 98% rename from crates/llm-engine/src/state.rs rename to crates/agen/src/state.rs index 836857fb..5d1b5172 100644 --- a/crates/llm-engine/src/state.rs +++ b/crates/agen/src/state.rs @@ -24,7 +24,7 @@ mod private { /// # Examples /// /// ```ignore -/// use llm_engine::Engine; +/// use agen::Engine; /// /// let mut engine = Engine::new(client) /// .system_prompt("You are helpful."); diff --git a/crates/llm-engine/src/timeline/event.rs b/crates/agen/src/timeline/event.rs similarity index 100% rename from crates/llm-engine/src/timeline/event.rs rename to crates/agen/src/timeline/event.rs diff --git a/crates/llm-engine/src/timeline/mod.rs b/crates/agen/src/timeline/mod.rs similarity index 100% rename from crates/llm-engine/src/timeline/mod.rs rename to crates/agen/src/timeline/mod.rs diff --git a/crates/llm-engine/src/timeline/text_block_collector.rs b/crates/agen/src/timeline/text_block_collector.rs similarity index 100% rename from crates/llm-engine/src/timeline/text_block_collector.rs rename to crates/agen/src/timeline/text_block_collector.rs diff --git a/crates/llm-engine/src/timeline/thinking_block_collector.rs b/crates/agen/src/timeline/thinking_block_collector.rs similarity index 100% rename from crates/llm-engine/src/timeline/thinking_block_collector.rs rename to crates/agen/src/timeline/thinking_block_collector.rs diff --git a/crates/llm-engine/src/timeline/timeline.rs b/crates/agen/src/timeline/timeline.rs similarity index 99% rename from crates/llm-engine/src/timeline/timeline.rs rename to crates/agen/src/timeline/timeline.rs index e7970ded..f972ec31 100644 --- a/crates/llm-engine/src/timeline/timeline.rs +++ b/crates/agen/src/timeline/timeline.rs @@ -348,7 +348,7 @@ where /// # Examples /// /// ```ignore -/// use llm_engine::{Timeline, Handler, TextBlockKind, TextBlockEvent}; +/// use agen::{Timeline, Handler, TextBlockKind, TextBlockEvent}; /// /// struct MyHandler; /// impl Handler for MyHandler { diff --git a/crates/llm-engine/src/timeline/tool_call_collector.rs b/crates/agen/src/timeline/tool_call_collector.rs similarity index 100% rename from crates/llm-engine/src/timeline/tool_call_collector.rs rename to crates/agen/src/timeline/tool_call_collector.rs diff --git a/crates/llm-engine/src/token_counter.rs b/crates/agen/src/token_counter.rs similarity index 100% rename from crates/llm-engine/src/token_counter.rs rename to crates/agen/src/token_counter.rs diff --git a/crates/llm-engine/src/tool.rs b/crates/agen/src/tool.rs similarity index 99% rename from crates/llm-engine/src/tool.rs rename to crates/agen/src/tool.rs index 62df16fa..6fe66fa6 100644 --- a/crates/llm-engine/src/tool.rs +++ b/crates/agen/src/tool.rs @@ -364,7 +364,7 @@ impl Default for ToolExecutionContext { /// # Manual Implementation /// /// ```ignore -/// use llm_engine::tool::{Tool, ToolError, ToolExecutionContext, ToolMeta, ToolDefinition, ToolOutput}; +/// use agen::tool::{Tool, ToolError, ToolExecutionContext, ToolMeta, ToolDefinition, ToolOutput}; /// use std::sync::Arc; /// /// struct MyTool { counter: std::sync::atomic::AtomicUsize } diff --git a/crates/llm-engine/src/tool_server.rs b/crates/agen/src/tool_server.rs similarity index 100% rename from crates/llm-engine/src/tool_server.rs rename to crates/agen/src/tool_server.rs diff --git a/crates/llm-engine/src/usage_record.rs b/crates/agen/src/usage_record.rs similarity index 100% rename from crates/llm-engine/src/usage_record.rs rename to crates/agen/src/usage_record.rs diff --git a/crates/llm-engine/tests/anthropic_fixtures.rs b/crates/agen/tests/anthropic_fixtures.rs similarity index 100% rename from crates/llm-engine/tests/anthropic_fixtures.rs rename to crates/agen/tests/anthropic_fixtures.rs diff --git a/crates/llm-engine/tests/callback_test.rs b/crates/agen/tests/callback_test.rs similarity index 96% rename from crates/llm-engine/tests/callback_test.rs rename to crates/agen/tests/callback_test.rs index 35852906..d3f9b60e 100644 --- a/crates/llm-engine/tests/callback_test.rs +++ b/crates/agen/tests/callback_test.rs @@ -8,13 +8,13 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; use std::time::Duration; +use agen::Engine; +use agen::llm_client::event::{Event, ResponseStatus, StatusEvent as ClientStatusEvent}; +use agen::llm_client::retry::RetryPolicy; +use agen::llm_client::{ClientError, LlmClient, Request, ResponseStream}; +use agen::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput}; use async_trait::async_trait; use common::MockLlmClient; -use llm_engine::Engine; -use llm_engine::llm_client::event::{Event, ResponseStatus, StatusEvent as ClientStatusEvent}; -use llm_engine::llm_client::retry::RetryPolicy; -use llm_engine::llm_client::{ClientError, LlmClient, Request, ResponseStream}; -use llm_engine::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput}; #[derive(Clone)] struct FailOnceClient { @@ -221,7 +221,7 @@ impl Tool for FixedOutputTool { async fn execute( &self, _input_json: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { Ok(self.output.clone()) } @@ -260,6 +260,7 @@ async fn test_callback_tool_result_events() { ToolOutput { summary: "did the thing".into(), content: Some("full detail body".into()), + attachments: Vec::new(), }, )); @@ -296,7 +297,7 @@ impl Tool for ErroringTool { async fn execute( &self, _input_json: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { Err(ToolError::ExecutionFailed(self.message.clone())) } diff --git a/crates/llm-engine/tests/common/mod.rs b/crates/agen/tests/common/mod.rs similarity index 96% rename from crates/llm-engine/tests/common/mod.rs rename to crates/agen/tests/common/mod.rs index 2c52d200..81902e78 100644 --- a/crates/llm-engine/tests/common/mod.rs +++ b/crates/agen/tests/common/mod.rs @@ -6,11 +6,11 @@ use std::path::{Path, PathBuf}; use std::pin::Pin; use std::sync::{Arc, Mutex}; +use agen::llm_client::event::{BlockType, DeltaContent, Event}; +use agen::llm_client::{ClientError, LlmClient, Request}; +use agen::timeline::{Handler, TextBlockEvent, TextBlockKind, Timeline}; use async_trait::async_trait; use futures::Stream; -use llm_engine::llm_client::event::{BlockType, DeltaContent, Event}; -use llm_engine::llm_client::{ClientError, LlmClient, Request}; -use llm_engine::timeline::{Handler, TextBlockEvent, TextBlockKind, Timeline}; use std::sync::atomic::{AtomicUsize, Ordering}; @@ -272,7 +272,7 @@ pub fn assert_timeline_integration(subdir: &str) { }); for event in &events { - let timeline_event: llm_engine::timeline::event::Event = event.clone().into(); + let timeline_event: agen::timeline::event::Event = event.clone().into(); timeline.dispatch(&timeline_event); } diff --git a/crates/llm-engine/tests/compile_fail.rs b/crates/agen/tests/compile_fail.rs similarity index 100% rename from crates/llm-engine/tests/compile_fail.rs rename to crates/agen/tests/compile_fail.rs diff --git a/crates/llm-engine/tests/engine_fixtures.rs b/crates/agen/tests/engine_fixtures.rs similarity index 94% rename from crates/llm-engine/tests/engine_fixtures.rs rename to crates/agen/tests/engine_fixtures.rs index c951ca62..1ab9e9ef 100644 --- a/crates/llm-engine/tests/engine_fixtures.rs +++ b/crates/agen/tests/engine_fixtures.rs @@ -9,10 +9,10 @@ use std::path::Path; use std::sync::Arc; use std::sync::atomic::{AtomicUsize, Ordering}; +use agen::Engine; +use agen::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput}; use async_trait::async_trait; use common::MockLlmClient; -use llm_engine::Engine; -use llm_engine::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput}; /// Fixture directory path fn fixtures_dir() -> std::path::PathBuf { @@ -61,7 +61,7 @@ impl Tool for MockWeatherTool { async fn execute( &self, input_json: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { self.call_count.fetch_add(1, Ordering::SeqCst); @@ -102,7 +102,7 @@ fn test_mock_client_from_fixture() { /// Creates a client with programmatically constructed events instead of using fixture files. #[test] fn test_mock_client_from_events() { - use llm_engine::llm_client::event::Event; + use agen::llm_client::event::Event; // Specify events directly let events = vec![ @@ -180,7 +180,7 @@ async fn test_engine_tool_call() { /// Useful when test independence is needed and external file dependency should be eliminated. #[tokio::test] async fn test_engine_with_programmatic_events() { - use llm_engine::llm_client::event::{Event, ResponseStatus, StatusEvent}; + use agen::llm_client::event::{Event, ResponseStatus, StatusEvent}; // Construct event sequence programmatically let events = vec![ @@ -208,8 +208,8 @@ async fn test_engine_with_programmatic_events() { /// correctly extracts id, name, and input (JSON). #[tokio::test] async fn test_tool_call_collector_integration() { - use llm_engine::llm_client::event::Event; - use llm_engine::timeline::{Timeline, ToolCallCollector}; + use agen::llm_client::event::Event; + use agen::timeline::{Timeline, ToolCallCollector}; // Event sequence containing ToolUse block let events = vec![ @@ -225,7 +225,7 @@ async fn test_tool_call_collector_integration() { // Dispatch events for event in &events { - let timeline_event: llm_engine::timeline::event::Event = event.clone().into(); + let timeline_event: agen::timeline::event::Event = event.clone().into(); timeline.dispatch(&timeline_event); } diff --git a/crates/llm-engine/tests/engine_state_test.rs b/crates/agen/tests/engine_state_test.rs similarity index 98% rename from crates/llm-engine/tests/engine_state_test.rs rename to crates/agen/tests/engine_state_test.rs index 0ce02700..794fa88d 100644 --- a/crates/llm-engine/tests/engine_state_test.rs +++ b/crates/agen/tests/engine_state_test.rs @@ -8,12 +8,12 @@ mod common; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; +use agen::Item; +use agen::llm_client::event::{Event, ResponseStatus, StatusEvent}; +use agen::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput}; +use agen::{Engine, EngineError}; use async_trait::async_trait; use common::MockLlmClient; -use llm_engine::Item; -use llm_engine::llm_client::event::{Event, ResponseStatus, StatusEvent}; -use llm_engine::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput}; -use llm_engine::{Engine, EngineError}; // ============================================================================= // Mutable State Tests @@ -150,7 +150,7 @@ impl Tool for CountingTool { async fn execute( &self, _input_json: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { self.calls.fetch_add(1, Ordering::SeqCst); Ok(format!("{}-ok", self.name).into()) diff --git a/crates/llm-engine/tests/fixtures/anthropic/anthropic_1767624445.jsonl b/crates/agen/tests/fixtures/anthropic/anthropic_1767624445.jsonl similarity index 100% rename from crates/llm-engine/tests/fixtures/anthropic/anthropic_1767624445.jsonl rename to crates/agen/tests/fixtures/anthropic/anthropic_1767624445.jsonl diff --git a/crates/llm-engine/tests/fixtures/anthropic/simple_text.jsonl b/crates/agen/tests/fixtures/anthropic/simple_text.jsonl similarity index 100% rename from crates/llm-engine/tests/fixtures/anthropic/simple_text.jsonl rename to crates/agen/tests/fixtures/anthropic/simple_text.jsonl diff --git a/crates/llm-engine/tests/fixtures/anthropic/tool_call.jsonl b/crates/agen/tests/fixtures/anthropic/tool_call.jsonl similarity index 100% rename from crates/llm-engine/tests/fixtures/anthropic/tool_call.jsonl rename to crates/agen/tests/fixtures/anthropic/tool_call.jsonl diff --git a/crates/llm-engine/tests/fixtures/gemini/long_text.jsonl b/crates/agen/tests/fixtures/gemini/long_text.jsonl similarity index 100% rename from crates/llm-engine/tests/fixtures/gemini/long_text.jsonl rename to crates/agen/tests/fixtures/gemini/long_text.jsonl diff --git a/crates/llm-engine/tests/fixtures/gemini/simple_text.jsonl b/crates/agen/tests/fixtures/gemini/simple_text.jsonl similarity index 100% rename from crates/llm-engine/tests/fixtures/gemini/simple_text.jsonl rename to crates/agen/tests/fixtures/gemini/simple_text.jsonl diff --git a/crates/llm-engine/tests/fixtures/gemini/tool_call.jsonl b/crates/agen/tests/fixtures/gemini/tool_call.jsonl similarity index 100% rename from crates/llm-engine/tests/fixtures/gemini/tool_call.jsonl rename to crates/agen/tests/fixtures/gemini/tool_call.jsonl diff --git a/crates/llm-engine/tests/fixtures/ollama/long_text.jsonl b/crates/agen/tests/fixtures/ollama/long_text.jsonl similarity index 100% rename from crates/llm-engine/tests/fixtures/ollama/long_text.jsonl rename to crates/agen/tests/fixtures/ollama/long_text.jsonl diff --git a/crates/llm-engine/tests/fixtures/ollama/simple_text.jsonl b/crates/agen/tests/fixtures/ollama/simple_text.jsonl similarity index 100% rename from crates/llm-engine/tests/fixtures/ollama/simple_text.jsonl rename to crates/agen/tests/fixtures/ollama/simple_text.jsonl diff --git a/crates/llm-engine/tests/fixtures/ollama/tool_call.jsonl b/crates/agen/tests/fixtures/ollama/tool_call.jsonl similarity index 100% rename from crates/llm-engine/tests/fixtures/ollama/tool_call.jsonl rename to crates/agen/tests/fixtures/ollama/tool_call.jsonl diff --git a/crates/llm-engine/tests/fixtures/openai/long_text.jsonl b/crates/agen/tests/fixtures/openai/long_text.jsonl similarity index 100% rename from crates/llm-engine/tests/fixtures/openai/long_text.jsonl rename to crates/agen/tests/fixtures/openai/long_text.jsonl diff --git a/crates/llm-engine/tests/fixtures/openai/openai_1767708975.jsonl b/crates/agen/tests/fixtures/openai/openai_1767708975.jsonl similarity index 100% rename from crates/llm-engine/tests/fixtures/openai/openai_1767708975.jsonl rename to crates/agen/tests/fixtures/openai/openai_1767708975.jsonl diff --git a/crates/llm-engine/tests/fixtures/openai/simple_text.jsonl b/crates/agen/tests/fixtures/openai/simple_text.jsonl similarity index 100% rename from crates/llm-engine/tests/fixtures/openai/simple_text.jsonl rename to crates/agen/tests/fixtures/openai/simple_text.jsonl diff --git a/crates/llm-engine/tests/fixtures/openai/tool_call.jsonl b/crates/agen/tests/fixtures/openai/tool_call.jsonl similarity index 100% rename from crates/llm-engine/tests/fixtures/openai/tool_call.jsonl rename to crates/agen/tests/fixtures/openai/tool_call.jsonl diff --git a/crates/llm-engine/tests/gemini_fixtures.rs b/crates/agen/tests/gemini_fixtures.rs similarity index 100% rename from crates/llm-engine/tests/gemini_fixtures.rs rename to crates/agen/tests/gemini_fixtures.rs diff --git a/crates/llm-engine/tests/ollama_fixtures.rs b/crates/agen/tests/ollama_fixtures.rs similarity index 100% rename from crates/llm-engine/tests/ollama_fixtures.rs rename to crates/agen/tests/ollama_fixtures.rs diff --git a/crates/llm-engine/tests/openai_fixtures.rs b/crates/agen/tests/openai_fixtures.rs similarity index 100% rename from crates/llm-engine/tests/openai_fixtures.rs rename to crates/agen/tests/openai_fixtures.rs diff --git a/crates/llm-engine/tests/parallel_execution_test.rs b/crates/agen/tests/parallel_execution_test.rs similarity index 98% rename from crates/llm-engine/tests/parallel_execution_test.rs rename to crates/agen/tests/parallel_execution_test.rs index d048db3e..a7067377 100644 --- a/crates/llm-engine/tests/parallel_execution_test.rs +++ b/crates/agen/tests/parallel_execution_test.rs @@ -6,15 +6,13 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; use std::time::{Duration, Instant}; -use async_trait::async_trait; -use llm_engine::Engine; -use llm_engine::interceptor::{ - Interceptor, PostToolAction, PreToolAction, ToolCallInfo, ToolResultInfo, -}; -use llm_engine::llm_client::event::{Event, ResponseStatus, StatusEvent}; -use llm_engine::tool::{ +use agen::Engine; +use agen::interceptor::{Interceptor, PostToolAction, PreToolAction, ToolCallInfo, ToolResultInfo}; +use agen::llm_client::event::{Event, ResponseStatus, StatusEvent}; +use agen::tool::{ Tool, ToolDefinition, ToolError, ToolExecutionContext, ToolMeta, ToolOutput, ToolResult, }; +use async_trait::async_trait; mod common; use common::MockLlmClient; @@ -64,7 +62,7 @@ impl Tool for SlowTool { async fn execute( &self, _input_json: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { self.call_count.fetch_add(1, Ordering::SeqCst); tokio::time::sleep(Duration::from_millis(self.delay_ms)).await; @@ -468,7 +466,7 @@ async fn test_post_tool_call_modification() { async fn execute( &self, _: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { Ok("Original Result".to_string().into()) } @@ -565,7 +563,7 @@ async fn test_before_tool_call_synthetic_result_committed() { assert_eq!(blocked_clone.call_count(), 0, "Blocked tool should not run"); assert!(result.engine.history().iter().any(|item| matches!( item, - llm_engine::Item::ToolResult { + agen::Item::ToolResult { call_id, summary, is_error: true, diff --git a/crates/llm-engine/tests/reasoning_round_trip_test.rs b/crates/agen/tests/reasoning_round_trip_test.rs similarity index 96% rename from crates/llm-engine/tests/reasoning_round_trip_test.rs rename to crates/agen/tests/reasoning_round_trip_test.rs index fa121d2b..3b37a258 100644 --- a/crates/llm-engine/tests/reasoning_round_trip_test.rs +++ b/crates/agen/tests/reasoning_round_trip_test.rs @@ -13,13 +13,13 @@ mod common; -use common::MockLlmClient; -use llm_engine::Engine; -use llm_engine::Item; -use llm_engine::llm_client::event::{ +use agen::Engine; +use agen::Item; +use agen::llm_client::event::{ BlockMetadata, BlockStart, BlockStop, BlockType, Event, ReasoningBlockData, ResponseStatus, StatusEvent, }; +use common::MockLlmClient; fn reasoning_block(text: impl Into, data: ReasoningBlockData) -> Vec { vec![ @@ -28,9 +28,9 @@ fn reasoning_block(text: impl Into, data: ReasoningBlockData) -> Vec Result { ... } -/// } -/// ``` -/// -/// This generates: -/// - `GetUserArgs` struct (for arguments) -/// - `Tool_get_user` struct (Tool wrapper) -/// - `impl Tool for Tool_get_user` -/// - `impl MyApp { fn get_user_tool(&self) -> Tool_get_user }` -#[proc_macro_attribute] -pub fn tool_registry(_attr: TokenStream, item: TokenStream) -> TokenStream { - let mut impl_block = parse_macro_input!(item as ItemImpl); - let self_ty = &impl_block.self_ty; - - let mut generated_items = Vec::new(); - - for item in &mut impl_block.items { - if let ImplItem::Fn(method) = item { - // Look for #[tool] attribute - let mut is_tool = false; - - // Iterate through attributes to check for tool and remove it - method.attrs.retain(|attr| { - if attr.path().is_ident("tool") { - is_tool = true; - false // Remove the attribute - } else { - true - } - }); - - if is_tool { - let tool_impl = generate_tool_impl(self_ty, method); - generated_items.push(tool_impl); - } - } - } - - let expanded = quote! { - #impl_block - - #(#generated_items)* - }; - - TokenStream::from(expanded) -} - -/// Extract description from doc comments -fn extract_doc_comment(attrs: &[Attribute]) -> String { - let mut lines = Vec::new(); - - for attr in attrs { - if attr.path().is_ident("doc") { - if let Meta::NameValue(meta) = &attr.meta { - if let syn::Expr::Lit(expr_lit) = &meta.value { - if let Lit::Str(lit_str) = &expr_lit.lit { - let line = lit_str.value(); - // Remove only the leading space (after ///) - let trimmed = line.strip_prefix(' ').unwrap_or(&line); - lines.push(trimmed.to_string()); - } - } - } - } - } - - lines.join("\n") -} - -/// Extract description from #[description = "..."] attribute -fn extract_description_attr(attrs: &[syn::Attribute]) -> Option { - for attr in attrs { - if attr.path().is_ident("description") - && let Meta::NameValue(meta) = &attr.meta - && let syn::Expr::Lit(expr_lit) = &meta.value - && let Lit::Str(lit_str) = &expr_lit.lit - { - return Some(lit_str.value()); - } - } - None -} - -fn is_tool_execution_context_type(ty: &Type) -> bool { - let Type::Path(path) = ty else { - return false; - }; - path.path - .segments - .last() - .is_some_and(|segment| segment.ident == "ToolExecutionContext") -} - -/// Generate Tool implementation from a method -fn generate_tool_impl(self_ty: &Type, method: &syn::ImplItemFn) -> proc_macro2::TokenStream { - let sig = &method.sig; - let method_name = &sig.ident; - let tool_name = method_name.to_string(); - - // Generate struct names (convert to PascalCase) - let pascal_name = to_pascal_case(&method_name.to_string()); - let tool_struct_name = format_ident!("Tool{}", pascal_name); - let args_struct_name = format_ident!("{}Args", pascal_name); - let definition_name = format_ident!("{}_definition", method_name); - - // Get description from doc comments - let description = extract_doc_comment(&method.attrs); - let description = if description.is_empty() { - format!("Tool: {}", tool_name) - } else { - description - }; - - // Parse method arguments (excluding self). A parameter typed as - // ToolExecutionContext is supplied from the execution context and is not - // exposed in the JSON input schema. - let method_args: Vec<_> = sig - .inputs - .iter() - .filter_map(|arg| { - if let FnArg::Typed(pat_type) = arg { - Some(pat_type) - } else { - None // Exclude self - } - }) - .collect(); - let json_args: Vec<_> = method_args - .iter() - .copied() - .filter(|pat_type| !is_tool_execution_context_type(pat_type.ty.as_ref())) - .collect(); - - // Generate argument struct fields - let arg_fields: Vec<_> = json_args - .iter() - .map(|pat_type| { - let pat = &pat_type.pat; - let ty = &pat_type.ty; - let desc = extract_description_attr(&pat_type.attrs); - - // Extract identifier from pattern - let field_name = if let Pat::Ident(pat_ident) = pat.as_ref() { - &pat_ident.ident - } else { - panic!("Only simple identifiers are supported for tool arguments"); - }; - - // Convert #[description] to schemars doc if present - if let Some(desc_str) = desc { - quote! { - #[schemars(description = #desc_str)] - pub #field_name: #ty - } - } else { - quote! { - pub #field_name: #ty - } - } - }) - .collect(); - - // Code to expand method arguments in execute - let call_args: Vec<_> = method_args - .iter() - .map(|pat_type| { - if is_tool_execution_context_type(pat_type.ty.as_ref()) { - quote! { ctx.clone() } - } else if let Pat::Ident(pat_ident) = pat_type.pat.as_ref() { - let ident = &pat_ident.ident; - quote! { args.#ident } - } else { - panic!("Only simple identifiers are supported"); - } - }) - .collect(); - let method_call = if call_args.is_empty() { - quote! { self.ctx.#method_name() } - } else { - quote! { self.ctx.#method_name(#(#call_args),*) } - }; - - // Check if method is async - let is_async = sig.asyncness.is_some(); - - // Parse return type and determine if Result - let awaiter = if is_async { - quote! { .await } - } else { - quote! {} - }; - - // Determine if return type is Result - let result_handling = if is_result_type(&sig.output) { - quote! { - match result { - Ok(val) => Ok(format!("{:?}", val).into()), - Err(e) => Err(::llm_engine::tool::ToolError::ExecutionFailed(format!("{}", e))), - } - } - } else { - quote! { - Ok(format!("{:?}", result).into()) - } - }; - - // Create empty Args struct if no arguments - let args_struct_def = if arg_fields.is_empty() { - quote! { - #[derive(serde::Deserialize, schemars::JsonSchema)] - struct #args_struct_name {} - } - } else { - quote! { - #[derive(serde::Deserialize, schemars::JsonSchema)] - struct #args_struct_name { - #(#arg_fields),* - } - } - }; - - // Execute body handling for no arguments case - let execute_body = if json_args.is_empty() { - quote! { - // Allow empty JSON object even with no JSON arguments - let _: #args_struct_name = serde_json::from_str(input_json) - .unwrap_or(#args_struct_name {}); - - let result = #method_call #awaiter; - #result_handling - } - } else { - quote! { - let args: #args_struct_name = serde_json::from_str(input_json) - .map_err(|e| ::llm_engine::tool::ToolError::InvalidArgument(e.to_string()))?; - - let result = #method_call #awaiter; - #result_handling - } - }; - - quote! { - #args_struct_def - - #[derive(Clone)] - pub struct #tool_struct_name { - ctx: #self_ty, - } - - #[async_trait::async_trait] - impl ::llm_engine::tool::Tool for #tool_struct_name { - async fn execute(&self, input_json: &str, ctx: ::llm_engine::tool::ToolExecutionContext) -> Result<::llm_engine::tool::ToolOutput, ::llm_engine::tool::ToolError> { - let _ = &ctx; - #execute_body - } - } - - impl #self_ty { - /// Get ToolDefinition (for registering with Engine) - pub fn #definition_name(&self) -> ::llm_engine::tool::ToolDefinition { - let ctx = self.clone(); - ::std::sync::Arc::new(move || { - let schema = schemars::schema_for!(#args_struct_name); - let meta = ::llm_engine::tool::ToolMeta::new(#tool_name) - .description(#description) - .input_schema(serde_json::to_value(schema).unwrap_or(serde_json::json!({}))); - let tool: ::std::sync::Arc = - ::std::sync::Arc::new(#tool_struct_name { ctx: ctx.clone() }); - (meta, tool) - }) - } - } - } -} - -/// Determine if return type is Result -fn is_result_type(return_type: &ReturnType) -> bool { - match return_type { - ReturnType::Default => false, - ReturnType::Type(_, ty) => { - // For Type::Path, check if last segment is "Result" - if let Type::Path(type_path) = ty.as_ref() { - if let Some(segment) = type_path.path.segments.last() { - return segment.ident == "Result"; - } - } - false - } - } -} - -/// Convert snake_case to PascalCase -fn to_pascal_case(s: &str) -> String { - s.split('_') - .map(|part| { - let mut chars = part.chars(); - match chars.next() { - None => String::new(), - Some(first) => first.to_uppercase().chain(chars).collect(), - } - }) - .collect() -} - -/// Marker attribute. Does nothing here as it's processed by `tool_registry`. -#[proc_macro_attribute] -pub fn tool(_attr: TokenStream, item: TokenStream) -> TokenStream { - item -} - -/// Marker for argument attributes. Interpreted by `tool_registry` during parsing. -/// -/// # Example -/// ```ignore -/// #[tool] -/// async fn get_user( -/// &self, -/// #[description = "The ID of the user to retrieve"] user_id: String -/// ) -> Result { ... } -/// ``` -#[proc_macro_attribute] -pub fn description(_attr: TokenStream, item: TokenStream) -> TokenStream { - item -} diff --git a/crates/llm-engine/README.md b/crates/llm-engine/README.md deleted file mode 100644 index 57320289..00000000 --- a/crates/llm-engine/README.md +++ /dev/null @@ -1,32 +0,0 @@ -# llm-engine - -## Role - -`llm-engine` owns provider-independent model turn orchestration over committed history, tools, callbacks, retries, continuation, pruning, and compaction boundaries. - -## Boundaries - -Owns: - -- Engine history mutation and append contracts -- tool-call loop semantics -- pre-stream retry and stream-started continuation policy -- pruning/compaction coordination from the Engine perspective -- provider-neutral events/callbacks/interceptors - -Does not own: - -- Host application names, sockets, process lifecycle, or scope delegation -- Product CLI shape -- Provider catalog and secret resolution -- Durable application state outside engine history - -## Design notes - -The Engine is where turn lifecycle belongs because it sees history, in-flight usage, partial output, and tool-call state. It should not receive context-only volatile facts; model-affecting inputs must first be appended to history. - -## See also - -- [`../../docs/design/context-history.md`](../../docs/design/context-history.md) -- [`../../docs/design/compaction.md`](../../docs/design/compaction.md) -- [`../../docs/design/provider-model-boundary.md`](../../docs/design/provider-model-boundary.md) diff --git a/crates/llm-engine/docs/architecture.md b/crates/llm-engine/docs/architecture.md deleted file mode 100644 index b016f921..00000000 --- a/crates/llm-engine/docs/architecture.md +++ /dev/null @@ -1,73 +0,0 @@ -# llm-engine アーキテクチャ - -## 概要 - -llm-engineは3層構成でLLMとのインタラクションを管理する。 - -``` -┌─────────────────────────────────────────┐ -│ Engine (オーケストレーション) │ -│ ターンループ / フック / ツール実行 │ -│ Type-state: Mutable ↔ CacheLocked │ -└───────────┬─────────────────────────────┘ - │ -┌───────────▼─────────────────────────────┐ -│ Timeline (イベント処理) │ -│ Handler dispatch / Block collectors │ -└───────────┬─────────────────────────────┘ - │ -┌───────────▼─────────────────────────────┐ -│ LLM Client (プロトコル) │ -│ Provider (HTTP) / Scheme (変換) │ -│ Anthropic / OpenAI / Gemini / Ollama │ -└─────────────────────────────────────────┘ -``` - -## モジュール構成 - -| モジュール | 責務 | 要件 | -|---|---|---| -| `engine` | ターンループ、フック統合、ツール実行、Pause/Resume | R1, R4 | -| `state` | Type-state (Mutable/CacheLocked) | R2 | -| `hook` | Hook trait、10フックポイント | R3, R4 | -| `tool` / `tool_server` | ツール定義・登録・実行 | R3 | -| `timeline` | イベントストリーム処理、Handler dispatch | — | -| `handler` | Handler/Kind trait、ブロック別ハンドラ | — | -| `callback` | クロージャベースイベント購読(`on_text_block`, `on_usage` 等) | — | -| `llm_client` | LLMプロバイダへのHTTPリクエスト/ストリーミング | — | -| `llm_client/scheme` | プロバイダ固有ワイヤーフォーマット変換 | — | -| `llm_client/providers` | Anthropic, OpenAI, Gemini, Ollama実装 | — | - -## データフロー - -### リクエスト(送信) -``` -Engine.history (Vec) - → build_request() → Request { items, tools, config } - → Scheme.build_request() → プロバイダ固有JSON - → Provider.stream() → HTTP POST -``` - -### レスポンス(受信) -``` -HTTP SSE bytes - → Provider → SSE events - → Scheme.parse_event() → Event (統一型) - → Timeline.dispatch() → Handler.on_event() - → TextBlockCollector / ToolCallCollector - → Engine: 履歴に追加、ツール実行判定 -``` - -## 内部型 - -### Item (会話履歴の単位) -- `Item::Message` — テキストメッセージ (user/assistant) -- `Item::ToolCall` — ツール呼び出し -- `Item::ToolResult` — ツール実行結果 -- `Item::Reasoning` — 思考 (Extended Thinking) - -### Event (ストリーミングイベント) -- Meta: `Ping`, `Usage`, `Status`, `Error` -- Block: `BlockStart` → `BlockDelta`* → `BlockStop` / `BlockAbort` - -単一の `Event` 型が全層で共有される(`llm_client::event` で定義、他層はre-export)。 diff --git a/crates/llm-engine/docs/requirements.md b/crates/llm-engine/docs/requirements.md deleted file mode 100644 index d0f9909c..00000000 --- a/crates/llm-engine/docs/requirements.md +++ /dev/null @@ -1,48 +0,0 @@ -# llm-engine 要件 - -## 前提 - -a. userメッセージを追加しなくてもagentの途中ママ投げれば、AIはそれを自身の生成途中と認識して普通に継続する -b. KVキャッシュは速度・効率の面で有利で、コンテキストの事後改変はキャッシュヒット率を大幅に下げる -c. ツール・フックの基本的なスキーマ自動化を提供する - -## 要件 - -### R1: Resume/Pause - -メッセージの送信と生成のResume、一時停止/再開。 - -- `Engine::run()` でターンを開始 -- フックから `Pause` を返してターンを一時停止 -- `Engine::resume()` でユーザーメッセージを追加せず継続 -- AIは中断を認識せず、継続として処理する - -**実装**: `engine.rs` — `resume()`, `get_pending_tool_calls()`, `EngineResult::Paused` - -### R2: 暗黙的KVキャッシュ保証 - -キャッシュを破壊しうる操作を明示的にブロックせずとも、いつの間にかキャッシュ破壊してた状態にはしたくない。 - -- Type-stateパターン(`Mutable` / `CacheLocked`)でコンパイル時に保証 -- `Engine::lock()` でCacheLocked状態に遷移 -- CacheLocked状態ではシステムプロンプトや履歴の変更APIが型レベルで利用不可 -- `locked_prefix_len` でプレフィックスの不変性を追跡 - -**実装**: `state.rs` (sealed trait), `engine.rs` (state-specific impl blocks) - -### R3: ツール・フックスキーマ自動化 - -- `#[tool]` マクロでツール定義を自動生成 -- `#[tool_registry]` マクロでツールサーバーを自動構成 -- `Hook` traitで10種のフックポイント - -**実装**: `llm-engine-macros/`, `tool.rs`, `tool_server.rs`, `hook.rs` - -### R4: フックは上層の関心事 - -フックはLLMクライアント層ではなく、Engine(オーケストレーション)層に配置する。 - -- LLMクライアント (`llm_client/`) はストリーミングとプロトコルのみ -- Engine層でフック実行、ツール統合、Pause/Resume制御 - -**実装**: `engine.rs` (hook integration), `hook.rs` (trait definitions) diff --git a/crates/llm-engine/src/lib.rs b/crates/llm-engine/src/lib.rs deleted file mode 100644 index 96f8cb59..00000000 --- a/crates/llm-engine/src/lib.rs +++ /dev/null @@ -1,66 +0,0 @@ -//! llm-engine - LLM Engine Library -//! -//! Provides components for managing interactions with LLMs. -//! -//! # Main Components -//! -//! - [`Engine`] - Central component for managing LLM interactions -//! - [`tool::Tool`] - Tools that can be invoked by the LLM -//! - [`interceptor::Interceptor`] - Control-flow delegation for the execution loop -//! - Closure-based event callbacks via `Engine::on_text_block()`, `on_tool_use_block()`, etc. -//! -//! # Quick Start -//! -//! ```ignore -//! use llm_engine::{Engine, Item}; -//! -//! // Create a Engine -//! let mut engine = Engine::new(client) -//! .system_prompt("You are a helpful assistant."); -//! -//! // Register tools (optional) -//! // engine.register_tool(my_tool_definition)?; -//! -//! // Run the interaction -//! let history = engine.run("Hello!").await?; -//! ``` -//! -//! # Cache Protection -//! -//! `run()` automatically locks the cache. To edit state between turns, -//! call `unlock_cache()` first; the next `run()` re-locks automatically. -//! -//! ```ignore -//! engine.run("user input").await?; -//! engine.unlock_cache(); -//! engine.set_system_prompt("new prompt"); -//! engine.run("next input").await?; -//! ``` - -mod engine; -mod handler; -mod message; - -pub(crate) mod callback; -pub mod event; -pub mod interceptor; -pub mod llm_client; -pub mod providers; -pub mod prune; -pub mod state; -pub mod timeline; -pub mod token_counter; -pub mod tool; -pub mod tool_server; -pub mod usage_record; - -pub use callback::{TextBlockScope, ThinkingBlockScope, ToolUseBlockScope}; -pub use engine::{ - Engine, EngineConfig, EngineError, EngineResult, EngineRunOutput, LlmRetryNotice, - ToolRegistryError, -}; -pub use handler::ToolUseBlockStart; -pub use interceptor::Interceptor; -pub use message::{ContentPart, Item, Message, Role}; -pub use tool::{ToolCall, ToolExecutionContext, ToolOutputLimits, ToolResult}; -pub use usage_record::UsageRecord; diff --git a/crates/manifest/Cargo.toml b/crates/manifest/Cargo.toml index a2a57e57..c8150391 100644 --- a/crates/manifest/Cargo.toml +++ b/crates/manifest/Cargo.toml @@ -6,7 +6,7 @@ license.workspace = true [dependencies] arc-swap = "1" -llm-engine = { workspace = true } +agen = { workspace = true } protocol = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } diff --git a/crates/manifest/src/model.rs b/crates/manifest/src/model.rs index 55da0c99..9cb47de8 100644 --- a/crates/manifest/src/model.rs +++ b/crates/manifest/src/model.rs @@ -14,9 +14,9 @@ use std::path::PathBuf; use serde::{Deserialize, Serialize}; -// `ModelCapability` は `llm-engine` 側に定義される runtime 構造だが、 +// `ModelCapability` は `agen` 側に定義される runtime 構造だが、 // マニフェストで任意に override できるよう型だけ再エクスポートする。 -pub use llm_engine::llm_client::capability::{ModelCapability, ReasoningControl, ReasoningEffort}; +pub use agen::llm_client::capability::{ModelCapability, ReasoningControl, ReasoningEffort}; /// Worker マニフェストの `[model]` セクション。 /// diff --git a/crates/manifest/src/model_catalog.rs b/crates/manifest/src/model_catalog.rs index 62079d32..68581759 100644 --- a/crates/manifest/src/model_catalog.rs +++ b/crates/manifest/src/model_catalog.rs @@ -16,7 +16,7 @@ use std::path::{Path, PathBuf}; use crate::{AuthRef, ModelManifest, SchemeKind}; -use llm_engine::llm_client::capability::ModelCapability; +use agen::llm_client::capability::ModelCapability; use serde::{Deserialize, Serialize}; const BUILTIN_PROVIDERS: &str = include_str!("../../../resources/providers/builtin.toml"); diff --git a/crates/memory/Cargo.toml b/crates/memory/Cargo.toml index 9a37751a..36d4e95e 100644 --- a/crates/memory/Cargo.toml +++ b/crates/memory/Cargo.toml @@ -9,7 +9,7 @@ async-trait = { workspace = true } chrono = { version = "0.4", features = ["serde"] } libc = { workspace = true } lint-common = { workspace = true } -llm-engine = { workspace = true } +agen = { workspace = true } manifest = { workspace = true } schemars = { workspace = true } serde = { workspace = true, features = ["derive"] } diff --git a/crates/memory/README.md b/crates/memory/README.md index 6e7c8182..88982913 100644 --- a/crates/memory/README.md +++ b/crates/memory/README.md @@ -17,7 +17,7 @@ Owns: Does not own: - authoritative project records (`.yoi/tickets/`, git history) -- normal Worker turn orchestration (`llm-engine`) +- normal Worker turn orchestration (`agen`) - product CLI command shape (`yoi`) - curated workflow definitions (`workflow`) diff --git a/crates/memory/src/extract/input.rs b/crates/memory/src/extract/input.rs index 3d7335ef..9cc2b093 100644 --- a/crates/memory/src/extract/input.rs +++ b/crates/memory/src/extract/input.rs @@ -5,7 +5,7 @@ //! tool result は summary のみ)。conversation 全体を Markdown の単一 //! セクションとして渡し、抽出指示は system prompt 側に寄せる。 -use llm_engine::Item; +use agen::Item; /// 与えられた `items` を extract sub-Engine の最初の user 入力に整形する。 pub fn build_extract_input(items: &[Item]) -> String { @@ -26,9 +26,9 @@ fn render_items(items: &[Item]) -> String { match item { Item::Message { role, content, .. } => { let role_label = match role { - llm_engine::Role::User => "User", - llm_engine::Role::Assistant => "Assistant", - llm_engine::Role::System => "System", + agen::Role::User => "User", + agen::Role::Assistant => "Assistant", + agen::Role::System => "System", }; let text: String = content .iter() diff --git a/crates/memory/src/extract/tool.rs b/crates/memory/src/extract/tool.rs index 8feb136b..202953e8 100644 --- a/crates/memory/src/extract/tool.rs +++ b/crates/memory/src/extract/tool.rs @@ -7,8 +7,8 @@ use std::sync::{Arc, Mutex}; +use agen::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput}; use async_trait::async_trait; -use llm_engine::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput}; use crate::extract::payload::ExtractedPayload; @@ -57,7 +57,7 @@ impl Tool for WriteExtractedTool { async fn execute( &self, input_json: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { let payload: ExtractedPayload = serde_json::from_str(input_json).map_err(|e| { ToolError::InvalidArgument(format!("invalid write_extracted input: {e}")) @@ -107,7 +107,7 @@ pub fn write_extracted_tool(ctx: Arc) -> ToolDefinition { mod tests { use super::*; - use llm_engine::tool::Tool; + use agen::tool::Tool; #[tokio::test] async fn write_extracted_records_payload() { diff --git a/crates/plugin-pdk/tests/template.rs b/crates/plugin-pdk/tests/template.rs index 1e42270f..c972f99e 100644 --- a/crates/plugin-pdk/tests/template.rs +++ b/crates/plugin-pdk/tests/template.rs @@ -179,7 +179,7 @@ fn pdk_runtime_dependencies_are_guest_side_only() { let forbidden = [ "worker", "yoi-worker", - "llm-engine", + "agen", "tui", "yoi-tui", "client", diff --git a/crates/protocol/README.md b/crates/protocol/README.md index f582d3d9..e85403e1 100644 --- a/crates/protocol/README.md +++ b/crates/protocol/README.md @@ -16,7 +16,7 @@ Does not own: - Unix socket implementation details (`client`, `worker`) - TUI rendering (`tui`) -- Engine history semantics (`llm-engine`) +- Engine history semantics (`agen`) - durable storage (`session-store`, `session-store` worker metadata) ## Design notes diff --git a/crates/protocol/src/lib.rs b/crates/protocol/src/lib.rs index 0d2ae8e6..0c4ac348 100644 --- a/crates/protocol/src/lib.rs +++ b/crates/protocol/src/lib.rs @@ -376,7 +376,7 @@ pub enum Event { /// /// `turn` is the AgentTurn index from `Engine::turn_count`. /// - /// Currently retry is not yet implemented (`llm-engine-stream-continuation`) + /// Currently retry is not yet implemented (`agen-stream-continuation`) /// so AgentTurn and `LlmCall` fire 1:1, but the contract here is /// the AgentTurn boundary; consumers that want per-LLM-call signals /// must subscribe to `LlmCallStart` / `LlmCallEnd` instead. diff --git a/crates/session-metrics/README.md b/crates/session-metrics/README.md index 7358ec7f..54fead4c 100644 --- a/crates/session-metrics/README.md +++ b/crates/session-metrics/README.md @@ -14,7 +14,7 @@ Owns: Does not own: -- prompt context packing (`llm-engine`) +- prompt context packing (`agen`) - generated memory contents (`memory`) - provider billing semantics (`provider`) - UI status rendering (`tui`) diff --git a/crates/session-store/Cargo.toml b/crates/session-store/Cargo.toml index 267e4e0b..02f16fdb 100644 --- a/crates/session-store/Cargo.toml +++ b/crates/session-store/Cargo.toml @@ -7,7 +7,7 @@ license.workspace = true [dependencies] base64.workspace = true -llm-engine = { workspace = true } +agen = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } uuid = { workspace = true, features = ["v7", "serde"] } diff --git a/crates/session-store/src/event_trace.rs b/crates/session-store/src/event_trace.rs index 627b2339..46501512 100644 --- a/crates/session-store/src/event_trace.rs +++ b/crates/session-store/src/event_trace.rs @@ -7,7 +7,7 @@ //! //! Disabled by default. Enable via `SessionConfig::record_event_trace`. -use llm_engine::llm_client::event::Event; +use agen::llm_client::event::Event; use serde::{Deserialize, Serialize}; use serde_json::Value; diff --git a/crates/session-store/src/lib.rs b/crates/session-store/src/lib.rs index 93859748..28f5c708 100644 --- a/crates/session-store/src/lib.rs +++ b/crates/session-store/src/lib.rs @@ -40,10 +40,10 @@ pub mod system_item; pub mod worker_metadata; pub mod worker_session_store; +pub use agen::UsageRecord; +pub use agen::llm_client::types::{ContentPart, Item, Role}; pub use event_trace::{TraceEntry, TracePayload}; pub use fs_store::FsStore; -pub use llm_engine::UsageRecord; -pub use llm_engine::llm_client::types::{ContentPart, Item, Role}; pub use logged_item::{LoggedContentPart, LoggedItem, LoggedRole, from_logged, to_logged}; pub use segment::{ SegmentStartState, append_entry, append_system_item, classify_history_item, diff --git a/crates/session-store/src/logged_item.rs b/crates/session-store/src/logged_item.rs index 4a759755..f2d499e6 100644 --- a/crates/session-store/src/logged_item.rs +++ b/crates/session-store/src/logged_item.rs @@ -1,4 +1,4 @@ -//! Persistence-stable mirror of `llm_engine::Item`. +//! Persistence-stable mirror of `agen::Item`. //! //! `LogEntry` does not embed `Item` directly because that couples the on-disk //! schema to the LLM worker's internal type — a field rename or addition there @@ -12,11 +12,11 @@ //! `Reasoning::encrypted_content` is preserved because OpenAI Responses ZDR //! requires it on stateless re-send. -use base64::{Engine as _, engine::general_purpose::STANDARD}; -use llm_engine::{ +use agen::{ llm_client::types::{ContentPart, Item, Role}, tool::{Attachment, ImageAttachment}, }; +use base64::{Engine as _, engine::general_purpose::STANDARD}; use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as _}; fn is_false(value: &bool) -> bool { @@ -437,8 +437,8 @@ mod tests { "attached", None, false, - vec![llm_engine::tool::Attachment::Image( - llm_engine::tool::ImageAttachment::new( + vec![agen::tool::Attachment::Image( + agen::tool::ImageAttachment::new( "image/png", std::sync::Arc::<[u8]>::from(&b"secret-image-body"[..]), ), diff --git a/crates/session-store/src/segment.rs b/crates/session-store/src/segment.rs index df05b268..a0250ad6 100644 --- a/crates/session-store/src/segment.rs +++ b/crates/session-store/src/segment.rs @@ -9,9 +9,9 @@ use crate::segment_log::{self, LogEntry, SegmentOrigin}; use crate::store::{Store, StoreError}; use crate::system_item::SystemItem; use crate::{SegmentId, SessionId}; -use llm_engine::EngineResult; -use llm_engine::llm_client::RequestConfig; -use llm_engine::llm_client::types::Item; +use agen::EngineResult; +use agen::llm_client::RequestConfig; +use agen::llm_client::types::Item; use protocol::Segment; /// State snapshot for creating a SegmentStart entry. diff --git a/crates/session-store/src/segment_log.rs b/crates/session-store/src/segment_log.rs index 2427d41b..a97a1de6 100644 --- a/crates/session-store/src/segment_log.rs +++ b/crates/session-store/src/segment_log.rs @@ -9,8 +9,8 @@ //! ordered. Fork lineage references between segments use turn-number indices //! (`SegmentOrigin.at_turn_index`) rather than per-entry hashes. -use llm_engine::llm_client::types::{Item, RequestConfig}; -use llm_engine::{EngineResult, UsageRecord}; +use agen::llm_client::types::{Item, RequestConfig}; +use agen::{EngineResult, UsageRecord}; use protocol::{InvokeKind, Segment}; use serde::{Deserialize, Serialize}; @@ -482,8 +482,8 @@ mod tests { "attached", None, false, - vec![llm_engine::tool::Attachment::Image( - llm_engine::tool::ImageAttachment::new("image/png", b"durable-image".to_vec()), + vec![agen::tool::Attachment::Image( + agen::tool::ImageAttachment::new("image/png", b"durable-image".to_vec()), )], ) .into(), @@ -497,7 +497,7 @@ mod tests { Item::ToolResult { attachments, .. } if matches!( attachments.as_slice(), - [llm_engine::tool::Attachment::Image(image)] + [agen::tool::Attachment::Image(image)] if image.data() == b"durable-image" ) )); @@ -837,10 +837,10 @@ mod tests { assert_eq!(state.history.len(), 1); match &state.history[0] { Item::Message { role, content, .. } => { - assert!(matches!(role, llm_engine::Role::User)); + assert!(matches!(role, agen::Role::User)); assert_eq!(content.len(), 1); match &content[0] { - llm_engine::ContentPart::Text { text } => { + agen::ContentPart::Text { text } => { assert_eq!(text, "see line1\nline2@src/main.rs"); } other => panic!("unexpected content: {other:?}"), diff --git a/crates/session-store/src/system_item.rs b/crates/session-store/src/system_item.rs index 07986853..8ab59b56 100644 --- a/crates/session-store/src/system_item.rs +++ b/crates/session-store/src/system_item.rs @@ -18,7 +18,7 @@ //! preserved only on the log/wire side; the LLM still sees plain //! system-message text. -use llm_engine::llm_client::types::Item; +use agen::llm_client::types::Item; use protocol::WorkerEvent; use serde::{Deserialize, Serialize}; diff --git a/crates/session-store/tests/common/mod.rs b/crates/session-store/tests/common/mod.rs index 9e2fb909..b474c57e 100644 --- a/crates/session-store/tests/common/mod.rs +++ b/crates/session-store/tests/common/mod.rs @@ -4,10 +4,10 @@ use std::pin::Pin; use std::sync::Arc; use std::sync::atomic::{AtomicUsize, Ordering}; +use agen::llm_client::event::Event; +use agen::llm_client::{ClientError, LlmClient, Request}; use async_trait::async_trait; use futures::Stream; -use llm_engine::llm_client::event::Event; -use llm_engine::llm_client::{ClientError, LlmClient, Request}; /// A mock LLM client that replays pre-defined event sequences. #[derive(Clone)] diff --git a/crates/session-store/tests/fs_store_test.rs b/crates/session-store/tests/fs_store_test.rs index 95b21658..7838ad4d 100644 --- a/crates/session-store/tests/fs_store_test.rs +++ b/crates/session-store/tests/fs_store_test.rs @@ -1,5 +1,5 @@ -use llm_engine::EngineResult; -use llm_engine::llm_client::types::{Item, RequestConfig}; +use agen::EngineResult; +use agen::llm_client::types::{Item, RequestConfig}; use session_store::{ FsStore, LogEntry, Store, TraceEntry, collect_state, new_segment_id, new_session_id, }; @@ -177,9 +177,9 @@ fn trace_entries_in_separate_file() { turn: 0, llm_call: Some(0), payload: session_store::TracePayload::StreamEvent { - event: llm_engine::llm_client::event::Event::Ping( - llm_engine::llm_client::event::PingEvent { timestamp: None }, - ), + event: agen::llm_client::event::Event::Ping(agen::llm_client::event::PingEvent { + timestamp: None, + }), }, }; store.append_trace(sid, segid, &trace).unwrap(); diff --git a/crates/session-store/tests/session_test.rs b/crates/session-store/tests/session_test.rs index 7becb600..557f4272 100644 --- a/crates/session-store/tests/session_test.rs +++ b/crates/session-store/tests/session_test.rs @@ -2,13 +2,13 @@ mod common; use std::sync::Arc; +use agen::Engine; +use agen::interceptor::{Interceptor, TurnEndAction}; +use agen::llm_client::event::{Event, ResponseStatus, StatusEvent}; +use agen::llm_client::types::{Item, RequestConfig}; +use agen::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput}; use async_trait::async_trait; use common::MockLlmClient; -use llm_engine::Engine; -use llm_engine::interceptor::{Interceptor, TurnEndAction}; -use llm_engine::llm_client::event::{Event, ResponseStatus, StatusEvent}; -use llm_engine::llm_client::types::{Item, RequestConfig}; -use llm_engine::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput}; use session_store::{FsStore, LogEntry, SegmentStartState, Store, collect_state}; // ============================================================================= @@ -57,7 +57,7 @@ impl Tool for MockWeatherTool { async fn execute( &self, _input_json: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { Ok("Sunny, 25C".to_string().into()) } @@ -102,7 +102,7 @@ async fn run_and_persist( session_id: session_store::SessionId, segment_id: session_store::SegmentId, input: &str, -) -> (Engine, llm_engine::EngineResult) { +) -> (Engine, agen::EngineResult) { // Mirror Worker's run-entry contract: log the user input as segments // before the worker pushes its flattened user_message; save_delta // skips the resulting user_message item to avoid double-write. @@ -191,7 +191,7 @@ async fn session_run_logs_entries() { matches!( e, LogEntry::RunCompleted { - result: llm_engine::EngineResult::Finished, + result: agen::EngineResult::Finished, .. } ) @@ -291,7 +291,7 @@ async fn session_resume_after_pause() { .unwrap(); let (_worker, result) = run_and_persist(worker, &store, sid, segid, "Weather?").await; - assert!(matches!(result, llm_engine::EngineResult::Paused)); + assert!(matches!(result, agen::EngineResult::Paused)); // Check RunCompleted is Paused let entries = store.read_all(sid, segid).unwrap(); @@ -299,7 +299,7 @@ async fn session_resume_after_pause() { matches!( e, LogEntry::RunCompleted { - result: llm_engine::EngineResult::Paused, + result: agen::EngineResult::Paused, .. } ) diff --git a/crates/ticket/Cargo.toml b/crates/ticket/Cargo.toml index 1f7f2c52..4d72af57 100644 --- a/crates/ticket/Cargo.toml +++ b/crates/ticket/Cargo.toml @@ -9,7 +9,7 @@ project-record = { workspace = true } async-trait = { workspace = true } chrono = { version = "0.4", default-features = false, features = ["clock"] } fs4 = { workspace = true, features = ["sync"] } -llm-engine = { workspace = true } +agen = { workspace = true } schemars = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } diff --git a/crates/ticket/src/tool.rs b/crates/ticket/src/tool.rs index 87ee84b2..4781a0bf 100644 --- a/crates/ticket/src/tool.rs +++ b/crates/ticket/src/tool.rs @@ -6,8 +6,8 @@ use std::sync::Arc; +use agen::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput}; use async_trait::async_trait; -use llm_engine::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput}; use serde::{Deserialize, Serialize}; use serde_json::{Value, json}; @@ -909,7 +909,7 @@ impl Tool for TicketCreateTool { async fn execute( &self, input_json: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { let params: TicketCreateParams = parse_input("TicketCreate", input_json)?; if params @@ -962,7 +962,7 @@ impl Tool for TicketEditItemTool { async fn execute( &self, input_json: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { let params: TicketEditItemParams = parse_input("TicketEditItem", input_json)?; let body_replacement = match (params.old_string, params.new_string) { @@ -1009,7 +1009,7 @@ impl Tool for QueryTicketTool { async fn execute( &self, input_json: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { let params: QueryTicketParams = parse_input("QueryTicket", input_json)?; let (filter, state_filter, params_limit) = params @@ -1050,7 +1050,7 @@ impl Tool for ShowTicketTool { async fn execute( &self, input_json: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { let params: ShowTicketParams = parse_input("ShowTicket", input_json)?; let query = id_or_query(params.id, params.query)?; @@ -1106,7 +1106,7 @@ impl Tool for TicketCommentTool { async fn execute( &self, input_json: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { execute_ticket_thread_event( &self.backend, @@ -1124,7 +1124,7 @@ macro_rules! impl_ticket_thread_event_tool { async fn execute( &self, input_json: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { execute_ticket_thread_event(&self.backend, $name, $kind, input_json) } @@ -1149,7 +1149,7 @@ impl Tool for TicketMarkReadyTool { async fn execute( &self, input_json: &str, - ctx: llm_engine::tool::ToolExecutionContext, + ctx: agen::tool::ToolExecutionContext, ) -> Result { let params: TicketMarkReadyParams = parse_input("TicketMarkReady", input_json)?; let ticket = self @@ -1182,7 +1182,7 @@ impl Tool for TicketIntakeReadyTool { async fn execute( &self, input_json: &str, - ctx: llm_engine::tool::ToolExecutionContext, + ctx: agen::tool::ToolExecutionContext, ) -> Result { let params: TicketIntakeReadyParams = parse_input("TicketIntakeReady", input_json)?; let ticket = self @@ -1215,7 +1215,7 @@ impl Tool for TicketQueueTool { async fn execute( &self, input_json: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { let params: TicketQueueParams = parse_input("TicketQueue", input_json)?; let queued_by = default_author(); @@ -1234,7 +1234,7 @@ impl Tool for TicketWorkflowStateTool { async fn execute( &self, input_json: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { let params: TicketWorkflowStateParams = parse_input("TicketWorkflowState", input_json)?; let from = params.from.into_state(); @@ -1273,7 +1273,7 @@ impl Tool for TicketCloseTool { async fn execute( &self, input_json: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { let params: TicketCloseParams = parse_input("TicketClose", input_json)?; self.backend @@ -1294,7 +1294,7 @@ impl Tool for TicketRelationRecordTool { async fn execute( &self, input_json: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { let params: TicketRelationRecordParams = parse_input("TicketRelationRecord", input_json)?; let relation = NewTicketRelation { @@ -1322,7 +1322,7 @@ impl Tool for TicketRelationRemoveTool { async fn execute( &self, input_json: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { let params: TicketRelationRemoveParams = parse_input("TicketRelationRemove", input_json)?; let output = self @@ -1348,7 +1348,7 @@ impl Tool for TicketRelationQueryTool { async fn execute( &self, input_json: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { let params: TicketRelationQueryParams = parse_input("TicketRelationQuery", input_json)?; let limit = bounded(params.limit, DEFAULT_LIST_LIMIT, MAX_LIST_LIMIT); @@ -1386,7 +1386,7 @@ impl Tool for TicketOrchestrationPlanRecordTool { async fn execute( &self, input_json: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { let params: TicketOrchestrationPlanRecordParams = parse_input("TicketOrchestrationPlanRecord", input_json)?; @@ -1422,7 +1422,7 @@ impl Tool for TicketOrchestrationPlanQueryTool { async fn execute( &self, input_json: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { let params: TicketOrchestrationPlanQueryParams = parse_input("TicketOrchestrationPlanQuery", input_json)?; @@ -1463,7 +1463,7 @@ impl Tool for TicketDoctorTool { async fn execute( &self, input_json: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { let params: TicketDoctorParams = parse_input("TicketDoctor", input_json)?; let limit = bounded(params.limit, DEFAULT_DIAGNOSTIC_LIMIT, MAX_DIAGNOSTIC_LIMIT); @@ -1489,7 +1489,7 @@ impl Tool for TicketDependencyCheckTool { async fn execute( &self, input_json: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { let params: TicketDependencyCheckParams = parse_input("TicketDependencyCheck", input_json)?; let check = self diff --git a/crates/tools/Cargo.toml b/crates/tools/Cargo.toml index 9cb3ad2e..f10a6819 100644 --- a/crates/tools/Cargo.toml +++ b/crates/tools/Cargo.toml @@ -8,7 +8,7 @@ license.workspace = true async-trait = { workspace = true } fs-operation.workspace = true html5ever = "0.26" -llm-engine = { workspace = true } +agen = { workspace = true } manifest = { workspace = true } secrets = { workspace = true } markup5ever_rcdom = "0.2" diff --git a/crates/tools/README.md b/crates/tools/README.md index 4ee5fd52..2503bec6 100644 --- a/crates/tools/README.md +++ b/crates/tools/README.md @@ -16,7 +16,7 @@ Owns: Does not own: - manifest permission policy definition (`manifest`) -- Engine tool-loop semantics (`llm-engine`) +- Engine tool-loop semantics (`agen`) - Worker lifecycle decisions (`worker`) - UI presentation (`tui`) diff --git a/crates/tools/src/bash.rs b/crates/tools/src/bash.rs index 50fe8625..8224472b 100644 --- a/crates/tools/src/bash.rs +++ b/crates/tools/src/bash.rs @@ -1,8 +1,8 @@ use std::path::PathBuf; use std::sync::Arc; +use agen::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput}; use async_trait::async_trait; -use llm_engine::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput}; use schemars::JsonSchema; use serde::Deserialize; use workdir::{CommandHandle, CommandOutputRequest, CommandRequest, WorkdirSessionHandle}; @@ -43,7 +43,7 @@ impl Tool for BashTool { async fn execute( &self, input_json: &str, - ctx: llm_engine::tool::ToolExecutionContext, + ctx: agen::tool::ToolExecutionContext, ) -> Result { let params: BashParams = serde_json::from_str(input_json) .map_err(|error| ToolError::InvalidArgument(format!("invalid Bash input: {error}")))?; diff --git a/crates/tools/src/edit.rs b/crates/tools/src/edit.rs index 17e87dae..dc618130 100644 --- a/crates/tools/src/edit.rs +++ b/crates/tools/src/edit.rs @@ -3,8 +3,8 @@ use std::path::PathBuf; use std::sync::Arc; +use agen::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput}; use async_trait::async_trait; -use llm_engine::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput}; use serde::Deserialize; use crate::error::ToolsError; @@ -39,7 +39,7 @@ impl Tool for EditTool { async fn execute( &self, input_json: &str, - ctx: llm_engine::tool::ToolExecutionContext, + ctx: agen::tool::ToolExecutionContext, ) -> Result { let params: EditParams = serde_json::from_str(input_json) .map_err(|e| ToolError::InvalidArgument(format!("invalid Edit input: {e}")))?; diff --git a/crates/tools/src/error.rs b/crates/tools/src/error.rs index 70b95310..71192559 100644 --- a/crates/tools/src/error.rs +++ b/crates/tools/src/error.rs @@ -6,7 +6,7 @@ use std::path::PathBuf; -use llm_engine::tool::ToolError; +use agen::tool::ToolError; #[derive(Debug, thiserror::Error)] pub enum ToolsError { diff --git a/crates/tools/src/glob.rs b/crates/tools/src/glob.rs index 12543f79..f2e8616e 100644 --- a/crates/tools/src/glob.rs +++ b/crates/tools/src/glob.rs @@ -1,7 +1,7 @@ use std::sync::Arc; +use agen::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput}; use async_trait::async_trait; -use llm_engine::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput}; use schemars::JsonSchema; use serde::Deserialize; use workdir::{GlobRequest, WorkdirPath, WorkdirSessionHandle}; @@ -28,7 +28,7 @@ impl Tool for GlobTool { async fn execute( &self, input_json: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { let params: GlobParams = serde_json::from_str(input_json) .map_err(|error| ToolError::InvalidArgument(format!("invalid Glob input: {error}")))?; diff --git a/crates/tools/src/grep.rs b/crates/tools/src/grep.rs index af424a15..598015b6 100644 --- a/crates/tools/src/grep.rs +++ b/crates/tools/src/grep.rs @@ -1,7 +1,7 @@ use std::sync::Arc; +use agen::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput}; use async_trait::async_trait; -use llm_engine::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput}; use schemars::JsonSchema; use serde::Deserialize; use workdir::{GrepOutputMode, GrepRequest, WorkdirPath, WorkdirSessionHandle}; @@ -56,7 +56,7 @@ impl Tool for GrepTool { async fn execute( &self, input_json: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { let params: GrepParams = serde_json::from_str(input_json) .map_err(|error| ToolError::InvalidArgument(format!("invalid Grep input: {error}")))?; diff --git a/crates/tools/src/lib.rs b/crates/tools/src/lib.rs index df140220..7b838dad 100644 --- a/crates/tools/src/lib.rs +++ b/crates/tools/src/lib.rs @@ -38,7 +38,7 @@ pub fn core_builtin_tools( session: workdir::WorkdirSessionHandle, tracker: Tracker, bash_output_dir: std::path::PathBuf, -) -> Vec { +) -> Vec { use workdir::WorkdirSessionCapability; let capabilities = session.capabilities(); @@ -66,7 +66,7 @@ pub fn core_builtin_tools( pub fn read_only_builtin_tools( session: workdir::WorkdirSessionHandle, -) -> Vec { +) -> Vec { debug_assert_eq!( session.capabilities(), workdir::WorkdirSessionCapabilities::READ_ONLY, @@ -77,7 +77,7 @@ pub fn read_only_builtin_tools( pub fn web_builtin_tools( web_config: Option, -) -> Vec { +) -> Vec { vec![ web_search_tool(web::WebTools::new(web_config.clone())), web_fetch_tool(web::WebTools::new(web_config)), diff --git a/crates/tools/src/read.rs b/crates/tools/src/read.rs index 673b7b2f..e743c56f 100644 --- a/crates/tools/src/read.rs +++ b/crates/tools/src/read.rs @@ -2,8 +2,8 @@ use std::sync::Arc; +use agen::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput}; use async_trait::async_trait; -use llm_engine::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput}; use serde::Deserialize; use crate::error::ToolsError; @@ -40,7 +40,7 @@ impl Tool for ReadTool { async fn execute( &self, input_json: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { let params: ReadParams = serde_json::from_str(input_json) .map_err(|e| ToolError::InvalidArgument(format!("invalid Read input: {e}")))?; diff --git a/crates/tools/src/tracker.rs b/crates/tools/src/tracker.rs index 5c284bcb..a544aa67 100644 --- a/crates/tools/src/tracker.rs +++ b/crates/tools/src/tracker.rs @@ -45,7 +45,7 @@ use std::collections::{HashMap, VecDeque}; use std::path::{Component, Path, PathBuf}; -use llm_engine::tool::ToolExecutionContext; +use agen::tool::ToolExecutionContext; use std::sync::{Arc, Mutex}; use sha2::{Digest, Sha256}; diff --git a/crates/tools/src/view_image.rs b/crates/tools/src/view_image.rs index 5c48abe0..5c9adb52 100644 --- a/crates/tools/src/view_image.rs +++ b/crates/tools/src/view_image.rs @@ -2,10 +2,10 @@ use std::sync::Arc; -use async_trait::async_trait; -use llm_engine::tool::{ +use agen::tool::{ Attachment, ImageAttachment, Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput, }; +use async_trait::async_trait; use serde::Deserialize; use workdir::{ReadRequest, WorkdirPath, WorkdirSessionHandle}; @@ -32,7 +32,7 @@ impl Tool for ViewImageTool { async fn execute( &self, input_json: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { let input: ViewImageParams = serde_json::from_str(input_json).map_err(|error| { ToolError::InvalidArgument(format!("invalid ViewImage input: {error}")) diff --git a/crates/tools/src/web.rs b/crates/tools/src/web.rs index 0bfdd49e..08bda8f3 100644 --- a/crates/tools/src/web.rs +++ b/crates/tools/src/web.rs @@ -4,9 +4,9 @@ use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use std::sync::Arc; use std::time::Duration; +use agen::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput}; use async_trait::async_trait; use html5ever::tendril::TendrilSink; -use llm_engine::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput}; use manifest::{WebConfig, WebFetchConfig, WebSearchConfig, WebSearchProvider}; use markup5ever_rcdom::{Handle, NodeData, RcDom}; use reqwest::header::{CONTENT_LENGTH, CONTENT_TYPE, HeaderMap, LOCATION}; @@ -149,7 +149,7 @@ impl Tool for WebSearchTool { async fn execute( &self, input_json: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { let input: WebSearchInput = serde_json::from_str(input_json) .map_err(|e| ToolError::InvalidArgument(format!("invalid WebSearch input: {e}")))?; @@ -200,7 +200,7 @@ impl Tool for WebFetchTool { async fn execute( &self, input_json: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { let input: WebFetchInput = serde_json::from_str(input_json) .map_err(|e| ToolError::InvalidArgument(format!("invalid WebFetch input: {e}")))?; diff --git a/crates/tools/src/write.rs b/crates/tools/src/write.rs index ff9ff09d..08e5f038 100644 --- a/crates/tools/src/write.rs +++ b/crates/tools/src/write.rs @@ -3,8 +3,8 @@ use std::path::PathBuf; use std::sync::Arc; +use agen::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput}; use async_trait::async_trait; -use llm_engine::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput}; use serde::Deserialize; use crate::error::ToolsError; @@ -34,7 +34,7 @@ impl Tool for WriteTool { async fn execute( &self, input_json: &str, - ctx: llm_engine::tool::ToolExecutionContext, + ctx: agen::tool::ToolExecutionContext, ) -> Result { let params: WriteParams = serde_json::from_str(input_json) .map_err(|e| ToolError::InvalidArgument(format!("invalid Write input: {e}")))?; @@ -244,7 +244,7 @@ mod tests { #[tokio::test] async fn write_then_edit_same_file_same_batch_uses_call_order() { use crate::edit::edit_tool; - use llm_engine::tool::ToolExecutionContext; + use agen::tool::ToolExecutionContext; let (dir, fs, tracker) = setup(); let file = dir.path().join("ordered.txt"); @@ -279,7 +279,7 @@ mod tests { #[tokio::test] async fn failed_same_file_mutation_releases_guard_for_followup() { use crate::edit::edit_tool; - use llm_engine::tool::ToolExecutionContext; + use agen::tool::ToolExecutionContext; let (dir, fs, tracker) = setup(); let file = dir.path().join("release.txt"); diff --git a/crates/tools/tests/edge_cases.rs b/crates/tools/tests/edge_cases.rs index e87c7d0c..60a402fa 100644 --- a/crates/tools/tests/edge_cases.rs +++ b/crates/tools/tests/edge_cases.rs @@ -2,7 +2,7 @@ use std::sync::Arc; -use llm_engine::tool::{Tool, ToolDefinition}; +use agen::tool::{Tool, ToolDefinition}; use manifest::{Permission, Scope, ScopeConfig, ScopeRule}; use serde_json::json; use tempfile::TempDir; @@ -10,7 +10,7 @@ use tools::{Tracker, core_builtin_tools}; use workdir::{LocalWorkdirSession, WorkdirSessionHandle}; struct Registry { - entries: Vec<(llm_engine::tool::ToolMeta, Arc)>, + entries: Vec<(agen::tool::ToolMeta, Arc)>, } impl Registry { diff --git a/crates/tools/tests/integration.rs b/crates/tools/tests/integration.rs index c08170cc..4f8e8aa6 100644 --- a/crates/tools/tests/integration.rs +++ b/crates/tools/tests/integration.rs @@ -1,13 +1,13 @@ //! Cross-tool integration tests exercising `core_builtin_tools()` end-to-end. //! //! `ToolServerHandle::register_tool` / `flush_pending` are `pub(crate)` in -//! llm-engine, so from here we exercise the factories directly — the same +//! agen, so from here we exercise the factories directly — the same //! code path that `flush_pending()` runs at production time. use std::path::Path; use std::sync::Arc; -use llm_engine::tool::{Tool, ToolDefinition, ToolMeta}; +use agen::tool::{Tool, ToolDefinition, ToolMeta}; use manifest::{Permission, Scope, ScopeConfig, ScopeRule}; use serde_json::json; use tempfile::TempDir; @@ -62,13 +62,13 @@ fn setup() -> (TempDir, TempDir, Registry) { (dir, spill, reg) } -async fn call(tool: &Arc, input: serde_json::Value) -> llm_engine::tool::ToolOutput { +async fn call(tool: &Arc, input: serde_json::Value) -> agen::tool::ToolOutput { tool.execute(&input.to_string(), Default::default()) .await .expect("tool execution failed") } -async fn call_err(tool: &Arc, input: serde_json::Value) -> llm_engine::tool::ToolError { +async fn call_err(tool: &Arc, input: serde_json::Value) -> agen::tool::ToolError { tool.execute(&input.to_string(), Default::default()) .await .expect_err("expected error") @@ -114,14 +114,14 @@ async fn view_image_reads_scoped_bytes_into_durable_tool_detail() { let output = call(&tool, json!({ "path": "image.png" })).await; assert_eq!(output.attachments.len(), 1); - let llm_engine::tool::Attachment::Image(image) = &output.attachments[0]; + let agen::tool::Attachment::Image(image) = &output.attachments[0]; assert_eq!(image.mime_type(), "image/png"); assert_eq!(image.data(), png); let serialized = serde_json::to_string(&output).unwrap(); assert!(!serialized.contains("private-image-body")); assert!(serialized.contains("attachments")); - let restored: llm_engine::tool::ToolOutput = serde_json::from_str(&serialized).unwrap(); - let llm_engine::tool::Attachment::Image(restored_image) = &restored.attachments[0]; + let restored: agen::tool::ToolOutput = serde_json::from_str(&serialized).unwrap(); + let agen::tool::Attachment::Image(restored_image) = &restored.attachments[0]; assert_eq!(restored_image.data(), png); let escaped = call_err(&tool, json!({ "path": "../outside.png" })).await; diff --git a/crates/tui/Cargo.toml b/crates/tui/Cargo.toml index a66cf8e8..0c087539 100644 --- a/crates/tui/Cargo.toml +++ b/crates/tui/Cargo.toml @@ -27,7 +27,7 @@ ticket = { workspace = true } serde = { workspace = true, features = ["derive"] } worker = { path = "../worker" } pulldown-cmark = { version = "0.13.3", default-features = false } -llm-engine.workspace = true +agen.workspace = true [dev-dependencies] tempfile = { workspace = true } diff --git a/crates/tui/src/app.rs b/crates/tui/src/app.rs index afef38d0..45104695 100644 --- a/crates/tui/src/app.rs +++ b/crates/tui/src/app.rs @@ -2335,7 +2335,7 @@ impl App { match entry { session_store::LogEntry::SegmentStart { history, .. } => { for logged in history { - let item: llm_engine::Item = logged.into(); + let item: agen::Item = logged.into(); let item_value = serde_json::to_value(&item).expect("Item is Serialize"); self.push_history_item(&item_value); } @@ -2351,7 +2351,7 @@ impl App { } session_store::LogEntry::AssistantItem { item, .. } | session_store::LogEntry::ToolResult { item, .. } => { - let it: llm_engine::Item = item.into(); + let it: agen::Item = item.into(); let item_value = serde_json::to_value(&it).expect("Item is Serialize"); self.push_history_item(&item_value); } @@ -3136,7 +3136,7 @@ mod completion_flow_tests { is_dir: true, }, CompletionEntry { - value: "crates/llm-engine".into(), + value: "crates/agen".into(), is_dir: true, }, ]; @@ -3271,7 +3271,7 @@ mod completion_flow_tests { ts: session_store::segment_log::now_millis(), session_id: uuid::Uuid::nil(), system_prompt: None, - config: llm_engine::llm_client::RequestConfig::default(), + config: agen::llm_client::RequestConfig::default(), history: vec![], forked_from: None, compacted_from: None, @@ -3530,7 +3530,7 @@ mod completion_flow_tests { system_prompt: None, config: Default::default(), history: vec![session_store::LoggedItem::from( - &llm_engine::Item::system_message("[File: src/main.rs]\nfn main() {}"), + &agen::Item::system_message("[File: src/main.rs]\nfn main() {}"), )], forked_from: None, compacted_from: None, diff --git a/crates/tui/src/worker_list.rs b/crates/tui/src/worker_list.rs index d8537322..49236489 100644 --- a/crates/tui/src/worker_list.rs +++ b/crates/tui/src/worker_list.rs @@ -612,7 +612,7 @@ mod tests { use super::*; use std::sync::Arc; - use llm_engine::llm_client::types::RequestConfig; + use agen::llm_client::types::RequestConfig; use protocol::stream::JsonLineWriter; use session_store::FsWorkerStore; use session_store::{LogEntry, Store, new_segment_id, new_session_id}; diff --git a/crates/worker-runtime/Cargo.toml b/crates/worker-runtime/Cargo.toml index e073df2f..1b24d55b 100644 --- a/crates/worker-runtime/Cargo.toml +++ b/crates/worker-runtime/Cargo.toml @@ -48,7 +48,7 @@ workdir.workspace = true [dev-dependencies] futures.workspace = true -llm-engine.workspace = true +agen.workspace = true serial_test = "3.4.0" tempfile.workspace = true tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } diff --git a/crates/worker-runtime/src/worker_backend.rs b/crates/worker-runtime/src/worker_backend.rs index 2b1123c8..ed1538f4 100644 --- a/crates/worker-runtime/src/worker_backend.rs +++ b/crates/worker-runtime/src/worker_backend.rs @@ -2061,11 +2061,11 @@ mod tests { use crate::management::RuntimeOptions; use crate::observation::WorkerObservationCursor; use crate::working_directory::LocalGitWorktreeMaterializer; + use agen::Engine; + use agen::llm_client::event::{Event as LlmEvent, ResponseStatus, StatusEvent}; + use agen::llm_client::{ClientError, LlmClient, Request}; use async_trait::async_trait; use futures::{Stream, StreamExt}; - use llm_engine::Engine; - use llm_engine::llm_client::event::{Event as LlmEvent, ResponseStatus, StatusEvent}; - use llm_engine::llm_client::{ClientError, LlmClient, Request}; use manifest::{Scope, WorkerManifest}; use session_store::{LogEntry, WorkerMetadataStore}; diff --git a/crates/worker/Cargo.toml b/crates/worker/Cargo.toml index 23af9dd8..bf951b6c 100644 --- a/crates/worker/Cargo.toml +++ b/crates/worker/Cargo.toml @@ -11,7 +11,7 @@ default = [] [dependencies] async-trait = { workspace = true } clap = { version = "4.6.0", features = ["derive"] } -llm-engine = { workspace = true, features = ["codex"] } +agen = { workspace = true, features = ["codex"] } session-store = { workspace = true } secrets = { workspace = true } manifest = { workspace = true } diff --git a/crates/worker/README.md b/crates/worker/README.md index aee91b83..0c5015fd 100644 --- a/crates/worker/README.md +++ b/crates/worker/README.md @@ -2,7 +2,7 @@ ## Role -`worker` turns an `llm-engine` Engine into a named runtime entity with manifest configuration, scoped tools, session persistence, protocol handling, and Worker metadata integration. +`worker` turns an `agen` Engine into a named runtime entity with manifest configuration, scoped tools, session persistence, protocol handling, and Worker metadata integration. ## Boundaries @@ -16,7 +16,7 @@ Owns: Does not own: -- provider-specific wire formats (`provider` / `llm-engine` clients) +- provider-specific wire formats (`provider` / `agen` clients) - product CLI parsing (`yoi`) - TUI display authority (`tui`) - current-state storage schema outside Worker metadata (`session-store` worker metadata) diff --git a/crates/worker/src/compact/prune.rs b/crates/worker/src/compact/prune.rs index ffa6d9c4..0bba4395 100644 --- a/crates/worker/src/compact/prune.rs +++ b/crates/worker/src/compact/prune.rs @@ -11,11 +11,9 @@ //! `UsageTracker` にも stash しておき、後続の `LlmUsage` と組で //! `prune.post_request` を吐けるようにする。 -use llm_engine::Item; -use llm_engine::llm_client::client::LlmClient; -use llm_engine::prune::{ - PruneConfig, PruneDecision, PruneObserver, SavingsEstimator, TokenEstimator, -}; +use agen::Item; +use agen::llm_client::client::LlmClient; +use agen::prune::{PruneConfig, PruneDecision, PruneObserver, SavingsEstimator, TokenEstimator}; use session_metrics::Metric; use session_store::Store; diff --git a/crates/worker/src/compact/token_counter.rs b/crates/worker/src/compact/token_counter.rs index ea75f980..19cf52de 100644 --- a/crates/worker/src/compact/token_counter.rs +++ b/crates/worker/src/compact/token_counter.rs @@ -1,7 +1,7 @@ //! Compact / prune 専用のトークン会計補助。 //! //! 汎用部分(`prefix_bytes`, `tokens_at`, `total_tokens`, `total_tokens_at`)は -//! [`llm_engine::token_counter`] にあり、`UsageRecord` の列と現在の history から +//! [`agen::token_counter`] にあり、`UsageRecord` の列と現在の history から //! pure に推定する。本モジュールは compact / prune 固有のロジック //! (`split_for_retained`, `savings_for_prune`)と、Worker 上の公開 API に //! 限定する。 @@ -17,12 +17,12 @@ //! - 推定の出どころは [`EstimateSource`] で呼び出し側に明示する。 //! 課金判断には使えないが、compact / prune の閾値判定には十分な精度 -use llm_engine::llm_client::client::LlmClient; -use llm_engine::token_counter::{item_bytes, prefix_bytes, tokens_at}; -use llm_engine::{Item, UsageRecord}; +use agen::llm_client::client::LlmClient; +use agen::token_counter::{item_bytes, prefix_bytes, tokens_at}; +use agen::{Item, UsageRecord}; use session_store::Store; -pub use llm_engine::token_counter::{EstimateSource, TokenEstimate}; +pub use agen::token_counter::{EstimateSource, TokenEstimate}; use crate::Worker; @@ -188,7 +188,7 @@ pub(crate) fn token_estimates_for_prune_impl( /// Prune 射影(`ToolResult.content = None`)で節約されるトークン数の推定。 /// -/// `indices` は [`llm_engine::prune::prunable_indices`] が返す候補列を +/// `indices` は [`agen::prune::prunable_indices`] が返す候補列を /// 想定する。各候補の content バイト差分を合算し、usage 履歴由来の /// tokens/byte レートでトークン数に換算する。範囲を「丸ごと drop」する /// のではなく、item 自体(summary 等)は残したままの値を返す点が @@ -248,7 +248,7 @@ impl Worker { /// 最後の measurement と、その後に追加された未測定分の byte/4 外挿。 pub fn total_tokens(&self) -> TokenEstimate { let usage = self.usage_history(); - llm_engine::token_counter::total_tokens(self.history(), &usage) + agen::token_counter::total_tokens(self.history(), &usage) } /// 任意の history index 時点でのプロンプト全長推定。 @@ -259,7 +259,7 @@ impl Worker { /// pointer 以降に増えたプロンプト長を測るのに使う。 pub fn total_tokens_at(&self, history_len: usize) -> TokenEstimate { let usage = self.usage_history(); - llm_engine::token_counter::total_tokens_at(self.history(), &usage, history_len) + agen::token_counter::total_tokens_at(self.history(), &usage, history_len) } /// 末尾から `retained` トークン以上を残すための分割位置。 diff --git a/crates/worker/src/compact/usage_tracker.rs b/crates/worker/src/compact/usage_tracker.rs index 7b356374..0367fe6e 100644 --- a/crates/worker/src/compact/usage_tracker.rs +++ b/crates/worker/src/compact/usage_tracker.rs @@ -16,8 +16,8 @@ use std::sync::Mutex; -use llm_engine::UsageRecord; -use llm_engine::timeline::event::UsageEvent; +use agen::UsageRecord; +use agen::timeline::event::UsageEvent; /// One drained measurement: the underlying `UsageRecord` plus an optional /// `correlation_id` stamped by the prune projection (or any other future diff --git a/crates/worker/src/compact/worker.rs b/crates/worker/src/compact/worker.rs index 39f60886..31eeb840 100644 --- a/crates/worker/src/compact/worker.rs +++ b/crates/worker/src/compact/worker.rs @@ -21,10 +21,10 @@ use std::path::PathBuf; use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; use std::sync::{Arc, Mutex}; +use agen::Item; +use agen::interceptor::{Interceptor, PreRequestAction, PreToolAction, ToolCallInfo}; +use agen::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput, ToolResult}; use async_trait::async_trait; -use llm_engine::Item; -use llm_engine::interceptor::{Interceptor, PreRequestAction, PreToolAction, ToolCallInfo}; -use llm_engine::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput, ToolResult}; use serde::Deserialize; #[cfg(test)] use workdir::LocalWorkdirSession; @@ -162,7 +162,7 @@ impl Tool for SearchSessionLogTool { async fn execute( &self, input_json: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { let params: SearchSessionParams = serde_json::from_str(input_json).map_err(|e| { ToolError::InvalidArgument(format!("invalid search_session_log input: {e}")) @@ -243,7 +243,7 @@ impl Tool for ReadSessionItemsTool { async fn execute( &self, input_json: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { let params: ReadSessionParams = serde_json::from_str(input_json).map_err(|e| { ToolError::InvalidArgument(format!("invalid read_session_items input: {e}")) @@ -343,7 +343,7 @@ impl Tool for MarkReadRequiredTool { async fn execute( &self, input_json: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { let params: MarkParams = serde_json::from_str(input_json).map_err(|e| { ToolError::InvalidArgument(format!("invalid mark_read_required input: {e}")) @@ -414,7 +414,7 @@ impl Tool for AddReferenceTool { async fn execute( &self, input_json: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { let params: ReferenceParams = serde_json::from_str(input_json) .map_err(|e| ToolError::InvalidArgument(format!("invalid add_reference input: {e}")))?; @@ -444,7 +444,7 @@ impl Tool for WriteSummaryTool { async fn execute( &self, input_json: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { let params: SummaryParams = serde_json::from_str(input_json) .map_err(|e| ToolError::InvalidArgument(format!("invalid write_summary input: {e}")))?; @@ -603,7 +603,7 @@ impl CompactWorkerInterceptor { impl Interceptor for CompactWorkerInterceptor { async fn pre_llm_request(&self, context: &mut Vec) -> PreRequestAction { let records = self.usage_tracker.records(); - let estimate = llm_engine::token_counter::total_tokens(context, &records); + let estimate = agen::token_counter::total_tokens(context, &records); if estimate.tokens > self.max_input_tokens { return PreRequestAction::Cancel(format!( "compact worker input occupancy exceeded {} tokens", @@ -653,8 +653,8 @@ mod tests { Arc::new(LocalWorkdirSession::new(scope, tmp.to_path_buf())) } - fn make_usage(input: u64) -> llm_engine::timeline::event::UsageEvent { - llm_engine::timeline::event::UsageEvent { + fn make_usage(input: u64) -> agen::timeline::event::UsageEvent { + agen::timeline::event::UsageEvent { input_tokens: Some(input), output_tokens: Some(0), total_tokens: Some(input), diff --git a/crates/worker/src/controller.rs b/crates/worker/src/controller.rs index 80f5d5dd..e801107e 100644 --- a/crates/worker/src/controller.rs +++ b/crates/worker/src/controller.rs @@ -2,8 +2,8 @@ use std::path::{Path, PathBuf}; use std::sync::Arc; use std::sync::atomic::Ordering; -use llm_engine::EngineError; -use llm_engine::llm_client::client::LlmClient; +use agen::EngineError; +use agen::llm_client::client::LlmClient; use session_store::WorkerMetadataStore; use session_store::{LogEntry, SessionExtension, Store}; use tokio::sync::{broadcast, mpsc, oneshot}; diff --git a/crates/worker/src/discovery.rs b/crates/worker/src/discovery.rs index bb4fc7a0..6e31005b 100644 --- a/crates/worker/src/discovery.rs +++ b/crates/worker/src/discovery.rs @@ -14,9 +14,9 @@ use std::process::Stdio; use std::sync::Arc; use std::time::Duration; +use agen::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput}; use async_trait::async_trait; use client::WorkerRuntimeCommand; -use llm_engine::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput}; use manifest::{Permission, ScopeRule}; use protocol::stream::JsonLineReader; use protocol::{Event, Method, WorkerStatus}; @@ -843,7 +843,7 @@ where async fn execute( &self, _input_json: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { let items = self .discovery @@ -871,7 +871,7 @@ where async fn execute( &self, input_json: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { let input: WorkerNameInput = serde_json::from_str(input_json) .map_err(|e| ToolError::InvalidArgument(format!("invalid RestoreWorker input: {e}")))?; @@ -949,7 +949,7 @@ where async fn execute( &self, input_json: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { let input: SendToPeerWorkerInput = serde_json::from_str(input_json).map_err(|e| { ToolError::InvalidArgument(format!("invalid SendToPeerWorker input: {e}")) diff --git a/crates/worker/src/feature.rs b/crates/worker/src/feature.rs index 41b996b9..a3752967 100644 --- a/crates/worker/src/feature.rs +++ b/crates/worker/src/feature.rs @@ -16,10 +16,10 @@ use std::collections::{HashMap, HashSet}; use std::fmt; use std::sync::Arc; -use llm_engine::Engine; -use llm_engine::llm_client::client::LlmClient; -use llm_engine::state::Mutable; -use llm_engine::tool::ToolDefinition; +use agen::Engine; +use agen::llm_client::client::LlmClient; +use agen::state::Mutable; +use agen::tool::ToolDefinition; use serde::{Deserialize, Serialize}; use thiserror::Error; @@ -1732,10 +1732,10 @@ pub mod plugin; #[cfg(test)] mod tests { use super::*; + use agen::llm_client::{ClientError, Request, ResponseStream}; + use agen::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput}; use async_trait::async_trait; use futures::stream; - use llm_engine::llm_client::{ClientError, Request, ResponseStream}; - use llm_engine::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput}; use serde_json::json; use std::sync::atomic::{AtomicUsize, Ordering}; @@ -1760,7 +1760,7 @@ mod tests { async fn execute( &self, _input_json: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { Ok(ToolOutput::from("ok".to_string())) } diff --git a/crates/worker/src/feature/builtin/flow_transition.rs b/crates/worker/src/feature/builtin/flow_transition.rs index 8a8a9ac8..02b88d14 100644 --- a/crates/worker/src/feature/builtin/flow_transition.rs +++ b/crates/worker/src/feature/builtin/flow_transition.rs @@ -2,13 +2,13 @@ use std::collections::BTreeSet; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; +use agen::llm_client::client::LlmClient; +use agen::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput}; use async_trait::async_trait; use flow::{ ConditionVerdict, FlowTransitionAttempt, FlowTransitionResolution, FlowVerifierOutcome, TransitionConditionResult, TransitionId, }; -use llm_engine::llm_client::client::LlmClient; -use llm_engine::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput}; use manifest::{Scope, WorkerManifest}; use schemars::JsonSchema; use serde::Deserialize; @@ -199,7 +199,7 @@ impl Tool for RequestFlowTransitionTool { async fn execute( &self, input_json: &str, - _context: llm_engine::tool::ToolExecutionContext, + _context: agen::tool::ToolExecutionContext, ) -> Result { let params: RequestFlowTransitionParams = serde_json::from_str(input_json).map_err(|error| { @@ -350,7 +350,7 @@ impl Tool for FinishFlowVerificationTool { async fn execute( &self, input_json: &str, - _context: llm_engine::tool::ToolExecutionContext, + _context: agen::tool::ToolExecutionContext, ) -> Result { let params: FinishFlowVerificationParams = serde_json::from_str(input_json).map_err(|error| { @@ -630,16 +630,16 @@ impl FeatureModule for ReadOnlyFlowWorkdirFeature { mod tests { use std::sync::atomic::AtomicUsize; - use flow::{FlowAttemptStatus, FlowTransitionRejection, StateId, TransitionCheckSnapshot}; - use futures::stream; - use llm_engine::llm_client::client::{LlmClient, ResponseStream}; - use llm_engine::llm_client::error::ClientError; - use llm_engine::llm_client::event::{ + use agen::llm_client::client::{LlmClient, ResponseStream}; + use agen::llm_client::error::ClientError; + use agen::llm_client::event::{ BlockDelta, BlockMetadata, BlockStart, BlockStop, BlockType, DeltaContent, Event as LlmEvent, StopReason, }; - use llm_engine::llm_client::types::Request; - use llm_engine::tool::ToolExecutionContext; + use agen::llm_client::types::Request; + use agen::tool::ToolExecutionContext; + use flow::{FlowAttemptStatus, FlowTransitionRejection, StateId, TransitionCheckSnapshot}; + use futures::stream; use manifest::WorkerManifest; use protocol::Segment; use session_store::{LogEntry, SegmentId, SessionId, Store}; diff --git a/crates/worker/src/feature/builtin/manage_workdir.rs b/crates/worker/src/feature/builtin/manage_workdir.rs index 449717de..03c3ce1e 100644 --- a/crates/worker/src/feature/builtin/manage_workdir.rs +++ b/crates/worker/src/feature/builtin/manage_workdir.rs @@ -7,10 +7,8 @@ use std::sync::Arc; +use agen::tool::{Tool, ToolDefinition, ToolError, ToolExecutionContext, ToolMeta, ToolOutput}; use async_trait::async_trait; -use llm_engine::tool::{ - Tool, ToolDefinition, ToolError, ToolExecutionContext, ToolMeta, ToolOutput, -}; use serde::{Deserialize, Serialize}; use serde_json::json; use workdir::http::{WorkdirSessionOperation, WorkdirSessionOperationResult}; diff --git a/crates/worker/src/feature/builtin/manage_worker.rs b/crates/worker/src/feature/builtin/manage_worker.rs index e6f069d9..caf527bf 100644 --- a/crates/worker/src/feature/builtin/manage_worker.rs +++ b/crates/worker/src/feature/builtin/manage_worker.rs @@ -2,10 +2,8 @@ use std::sync::Arc; +use agen::tool::{Tool, ToolDefinition, ToolError, ToolExecutionContext, ToolMeta, ToolOutput}; use async_trait::async_trait; -use llm_engine::tool::{ - Tool, ToolDefinition, ToolError, ToolExecutionContext, ToolMeta, ToolOutput, -}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use uuid::Uuid; diff --git a/crates/worker/src/feature/builtin/memory.rs b/crates/worker/src/feature/builtin/memory.rs index b4eec17e..595eebc1 100644 --- a/crates/worker/src/feature/builtin/memory.rs +++ b/crates/worker/src/feature/builtin/memory.rs @@ -6,10 +6,8 @@ use std::sync::Arc; +use agen::tool::{Tool, ToolDefinition, ToolError, ToolExecutionContext, ToolMeta, ToolOutput}; use async_trait::async_trait; -use llm_engine::tool::{ - Tool, ToolDefinition, ToolError, ToolExecutionContext, ToolMeta, ToolOutput, -}; use memory::backend::{ MemoryBackendHttpResponse, MemoryBackendOperation, MemoryBackendOperationResult, MemoryConsolidateStagingOperation, MemoryConsolidationOutput, MemoryDocumentReadOperation, @@ -343,7 +341,7 @@ fn query_schema() -> serde_json::Value { #[cfg(test)] mod tests { use super::*; - use llm_engine::tool::ToolDefinition; + use agen::tool::ToolDefinition; fn test_client() -> Arc { Arc::new(crate::worker::TestWorkspaceHttpClient::new( diff --git a/crates/worker/src/feature/builtin/memory_extract.rs b/crates/worker/src/feature/builtin/memory_extract.rs index 9c6410b8..61400fe7 100644 --- a/crates/worker/src/feature/builtin/memory_extract.rs +++ b/crates/worker/src/feature/builtin/memory_extract.rs @@ -1,7 +1,7 @@ use std::sync::{Arc, Mutex}; +use agen::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput}; use async_trait::async_trait; -use llm_engine::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput}; use memory::backend::{ MemoryBackendOperation, MemoryBackendOperationResult, MemoryStageCandidateOperation, }; @@ -163,7 +163,7 @@ impl Tool for StageMemoryCandidateTool { async fn execute( &self, input_json: &str, - _context: llm_engine::tool::ToolExecutionContext, + _context: agen::tool::ToolExecutionContext, ) -> Result { let params: StageMemoryCandidateParams = serde_json::from_str(input_json).map_err(|error| { @@ -249,7 +249,7 @@ impl Tool for FinishMemoryExtractionTool { async fn execute( &self, input_json: &str, - _context: llm_engine::tool::ToolExecutionContext, + _context: agen::tool::ToolExecutionContext, ) -> Result { let params: FinishMemoryExtractionParams = serde_json::from_str(input_json).map_err(|error| { @@ -387,7 +387,7 @@ fn truncate_line(text: &str, max_chars: usize) -> String { #[cfg(test)] mod tests { - use llm_engine::Item; + use agen::Item; use super::*; @@ -451,7 +451,7 @@ mod tests { let error = tool .execute( r#"{"kind":"decision","claim":"claim","why_useful":"useful","entry_refs":["E00000009"]}"#, - llm_engine::tool::ToolExecutionContext::direct(), + agen::tool::ToolExecutionContext::direct(), ) .await .unwrap_err(); diff --git a/crates/worker/src/feature/builtin/merge_request.rs b/crates/worker/src/feature/builtin/merge_request.rs index 985cb41f..a97357f1 100644 --- a/crates/worker/src/feature/builtin/merge_request.rs +++ b/crates/worker/src/feature/builtin/merge_request.rs @@ -4,8 +4,8 @@ use crate::feature::{ ToolDeclaration, ToolDefinition, }; use crate::worker::{WorkspaceClient, WorkspaceRequest, WorkspaceRequestMethod}; +use agen::tool::{Tool, ToolError, ToolExecutionContext, ToolMeta, ToolOutput}; use async_trait::async_trait; -use llm_engine::tool::{Tool, ToolError, ToolExecutionContext, ToolMeta, ToolOutput}; use manifest::MergeRequestFeatureConfig; use schemars::JsonSchema; use serde::Deserialize; diff --git a/crates/worker/src/feature/builtin/objective.rs b/crates/worker/src/feature/builtin/objective.rs index a65bacfc..460c929a 100644 --- a/crates/worker/src/feature/builtin/objective.rs +++ b/crates/worker/src/feature/builtin/objective.rs @@ -7,10 +7,8 @@ use std::sync::Arc; +use agen::tool::{Tool, ToolDefinition, ToolError, ToolExecutionContext, ToolMeta, ToolOutput}; use async_trait::async_trait; -use llm_engine::tool::{ - Tool, ToolDefinition, ToolError, ToolExecutionContext, ToolMeta, ToolOutput, -}; use serde::{Deserialize, Serialize}; use serde_json::json; @@ -611,7 +609,7 @@ struct ObjectiveDetail { #[cfg(test)] mod tests { use super::*; - use llm_engine::tool::ToolDefinition; + use agen::tool::ToolDefinition; fn tool_names(definitions: Vec) -> Vec { let mut names = definitions diff --git a/crates/worker/src/feature/builtin/orchestration.rs b/crates/worker/src/feature/builtin/orchestration.rs index b0088ae1..1d623f39 100644 --- a/crates/worker/src/feature/builtin/orchestration.rs +++ b/crates/worker/src/feature/builtin/orchestration.rs @@ -2,10 +2,8 @@ use std::sync::Arc; +use agen::tool::{Tool, ToolDefinition, ToolError, ToolExecutionContext, ToolMeta, ToolOutput}; use async_trait::async_trait; -use llm_engine::tool::{ - Tool, ToolDefinition, ToolError, ToolExecutionContext, ToolMeta, ToolOutput, -}; use protocol::Segment; use schemars::JsonSchema; use serde::Deserialize; diff --git a/crates/worker/src/feature/builtin/session_explore.rs b/crates/worker/src/feature/builtin/session_explore.rs index 8e724e27..24e541ed 100644 --- a/crates/worker/src/feature/builtin/session_explore.rs +++ b/crates/worker/src/feature/builtin/session_explore.rs @@ -1,7 +1,7 @@ use std::sync::Arc; +use agen::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput}; use async_trait::async_trait; -use llm_engine::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput}; use schemars::JsonSchema; use serde::Deserialize; @@ -181,7 +181,7 @@ impl Tool for ShowOverviewTool { async fn execute( &self, input_json: &str, - _context: llm_engine::tool::ToolExecutionContext, + _context: agen::tool::ToolExecutionContext, ) -> Result { let params: ShowOverviewParams = parse_input("ShowOverview", input_json)?; let limit = bounded_limit(params.limit, DEFAULT_PAGE_LIMIT, MAX_PAGE_LIMIT); @@ -223,7 +223,7 @@ impl Tool for SearchEntriesTool { async fn execute( &self, input_json: &str, - _context: llm_engine::tool::ToolExecutionContext, + _context: agen::tool::ToolExecutionContext, ) -> Result { let params: SearchEntriesParams = parse_input("SearchEntries", input_json)?; let kind = params.kind.as_deref().map(parse_kind).transpose()?; @@ -286,7 +286,7 @@ impl Tool for ReadEntryTool { async fn execute( &self, input_json: &str, - _context: llm_engine::tool::ToolExecutionContext, + _context: agen::tool::ToolExecutionContext, ) -> Result { let params: ReadEntryParams = parse_input("ReadEntry", input_json)?; let entry_ref = parse_entry_ref(¶ms.entry_ref)?; @@ -390,7 +390,7 @@ fn json_output(summary: String, value: serde_json::Value) -> Result Result { let params: TaskCreateParams = serde_json::from_str(input_json) .map_err(|e| ToolError::InvalidArgument(format!("invalid TaskCreate input: {e}")))?; @@ -106,7 +106,7 @@ impl Tool for TaskListTool { async fn execute( &self, input_json: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { let params: TaskListParams = serde_json::from_str(input_json) .map_err(|e| ToolError::InvalidArgument(format!("invalid TaskList input: {e}")))?; @@ -127,7 +127,7 @@ impl Tool for TaskGetTool { async fn execute( &self, input_json: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { let params: TaskGetParams = serde_json::from_str(input_json) .map_err(|e| ToolError::InvalidArgument(format!("invalid TaskGet input: {e}")))?; @@ -149,7 +149,7 @@ impl Tool for TaskUpdateTool { async fn execute( &self, input_json: &str, - _ctx: llm_engine::tool::ToolExecutionContext, + _ctx: agen::tool::ToolExecutionContext, ) -> Result { let params: TaskUpdateParams = serde_json::from_str(input_json) .map_err(|e| ToolError::InvalidArgument(format!("invalid TaskUpdate input: {e}")))?; diff --git a/crates/worker/src/feature/builtin/ticket.rs b/crates/worker/src/feature/builtin/ticket.rs index cf873514..6a70263b 100644 --- a/crates/worker/src/feature/builtin/ticket.rs +++ b/crates/worker/src/feature/builtin/ticket.rs @@ -31,7 +31,7 @@ use crate::feature::{ ToolDefinition, }; use crate::worker::{WorkspaceClient, WorkspaceRequest, WorkspaceRequestMethod}; -use llm_engine::tool::{Tool, ToolError, ToolExecutionContext, ToolMeta, ToolOutput}; +use agen::tool::{Tool, ToolError, ToolExecutionContext, ToolMeta, ToolOutput}; #[derive(Clone, Copy)] enum WorkspaceTicketReadKind { @@ -1230,7 +1230,7 @@ mod tests { } fn pending_tool_description( - pending_tools: &[llm_engine::tool::ToolDefinition], + pending_tools: &[agen::tool::ToolDefinition], name: &str, ) -> String { pending_tools diff --git a/crates/worker/src/feature/builtin/worker_observation.rs b/crates/worker/src/feature/builtin/worker_observation.rs index 5c820c9e..2fdbe863 100644 --- a/crates/worker/src/feature/builtin/worker_observation.rs +++ b/crates/worker/src/feature/builtin/worker_observation.rs @@ -1,8 +1,8 @@ use std::sync::Arc; +use agen::Item; +use agen::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput}; use async_trait::async_trait; -use llm_engine::Item; -use llm_engine::tool::{Tool, ToolDefinition, ToolError, ToolMeta, ToolOutput}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use session_store::collect_state; @@ -495,7 +495,7 @@ impl Tool for ViewSessionOverviewTool { async fn execute( &self, input_json: &str, - _context: llm_engine::tool::ToolExecutionContext, + _context: agen::tool::ToolExecutionContext, ) -> Result { let params: ViewSessionOverviewParams = parse_input("ViewSessionOverview", input_json)?; let view = latest_view(&*self.provider, ¶ms.subject).await?; @@ -540,7 +540,7 @@ impl Tool for SearchSessionEntriesTool { async fn execute( &self, input_json: &str, - _context: llm_engine::tool::ToolExecutionContext, + _context: agen::tool::ToolExecutionContext, ) -> Result { let params: SearchSessionEntriesParams = parse_input("SearchSessionEntries", input_json)?; let view = latest_view(&*self.provider, ¶ms.subject).await?; @@ -598,7 +598,7 @@ impl Tool for ReadSessionEntryTool { async fn execute( &self, input_json: &str, - _context: llm_engine::tool::ToolExecutionContext, + _context: agen::tool::ToolExecutionContext, ) -> Result { let params: ReadSessionEntryParams = parse_input("ReadSessionEntry", input_json)?; let entry_ref = parse_entry_ref(¶ms.entry_ref)?; @@ -714,7 +714,7 @@ fn json_output(summary: String, value: serde_json::Value) -> Result