Metadata-Version: 2.4
Name: snowland-agent-core
Version: 0.2.0
Summary: Framework-agnostic agent engine shared by the snowland-aitool cloud web service and the local IDE MCP server.
Author: Snowland Co, .Ltd
License-Expression: BSD-3-Clause
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: aider-chat
Requires-Dist: langgraph
Requires-Dist: snowland-smx
Requires-Dist: astartool>=0.3
Provides-Extra: mcp
Requires-Dist: mcp>=1.0; extra == "mcp"
Requires-Dist: pydantic>=2.0; extra == "mcp"
Requires-Dist: anyio; extra == "mcp"
Requires-Dist: uvicorn>=0.30; extra == "mcp"
Dynamic: license-file

# snowland-agent-core

[![PyPI version](https://img.shields.io/pypi/v/snowland-agent-core.svg?cacheSeconds=86400)](https://pypi.org/project/snowland-agent-core/)
[![PyPI downloads](https://img.shields.io/pypi/dm/snowland-agent-core.svg?cacheSeconds=86400)](https://pypi.org/project/snowland-agent-core/)
[![Python versions](https://img.shields.io/pypi/pyversions/snowland-agent-core.svg?cacheSeconds=86400)](https://pypi.org/project/snowland-agent-core/)
[![License: BSD-3-Clause](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg?cacheSeconds=86400)](LICENSE)
[![Dependency: aider-chat](https://img.shields.io/badge/aider--chat-supported-green.svg?cacheSeconds=86400)](https://github.com/Aider-AI/aider)
[![Dependency: langgraph](https://img.shields.io/badge/langgraph-supported-green.svg?cacheSeconds=86400)](https://github.com/langchain-ai/langgraph)

Framework-agnostic agent engine shared by the **snowland-aitool** cloud web
service and the local IDE MCP server.

This package contains the agent engine (`AiderCore`) and a lightweight
multi-agent orchestration layer (`snowland_agent_core.orchestration`). Framework-
specific concerns (configuration, persistence, credential lookup, audit logging)
are supplied through dependency-injected *ports*, so the engine also runs under a
plain CLI or inside unit tests with in-memory adapters. The orchestration layer
is built on **LangGraph** (`langgraph.graph`).

## Features

- **Ports & Adapters.** `AiderCore` talks to abstract protocols
  (`CredentialRepo`, `SessionRepo`, `InvocationLog`). Concrete implementations
  are injected by the host (Django adapter or in-memory test doubles).
- **Single-agent engine.** `AiderCore.chat(...)` runs one aider turn with
  conversation persistence, skill injection, and tool-result plumbing.
- **Multi-agent orchestration.** `AgentUnit` / `AgentRegistry` / `Router` /
  `Pipeline` / `Team` compose agents. The `Team` coordinator is built on a
  LangGraph `StateGraph` with a `max_steps` cap.
- **Safety gates.** Input prompt-injection checks (`check_input`) and output
  secret-leak scans (`check_output`) wrap every run; the orchestration layer
  never bypasses the engine's gates.
- **Graph visualization.** `Team.draw_mermaid()` / `export_graph_mermaid()` render
  the LangGraph `StateGraph` via LangGraph's native Mermaid export; the
  dependency-free `team_to_mermaid()` renderer provides the same Mermaid
  flowchart without importing LangGraph.

## Architecture: three-repo topology

```
snowland-aitool-core/        # THIS repo — engine + orchestration
  snowland_agent_core/
    core/                    # AiderCore, SessionManager, ports, config, safety, ...
    orchestration/           # AgentUnit, Registry, Router, Pipeline, Team, ...

snowland-django-agent/       # Django adapter — implements the ports, reads settings
  snowland_django_agent/
    django_adapter/          # build_config(), get_core(), get_team(), repos
    models.py / auth.py / views.py / ...

snowland-aitool/             # Host — Django web service + MCP server
  mcp_server/server.py       # bridges MCP tools to the Django adapter
  aitool/settings.py         # TEAM_DEFAULT_MAX_STEPS and other tunables
```

The engine is the single source of truth for execution and safety. The Django
adapter and the MCP server are *adapters* that call into this package; they do
not reimplement agent logic.

## Installation

```bash
# Engine only (used by the cloud web service)
pip install .

# With the optional MCP extras (standalone MCP server)
pip install ".[mcp]"
```

Runtime dependencies: `aider-chat`, `langgraph`. The `mcp` extra adds
`mcp`, `pydantic`, `anyio`, `uvicorn`.

## Package layout

```
snowland_agent_core/
  __init__.py            # __version__, VERSION
  base/                  # 与领域无关的抽象层：不依赖 Django / aider / 任何 LLM SDK
    __init__.py          # 公共 base API（BaseAgent / SkillRegistry / ContextManager / Verifier / Sandbox ...）
    agent.py             # BaseAgent / AgentResult / AgentCapability
    ports.py             # CredentialRepo / SessionRepo / InvocationLog protocols
    context.py           # ContextManager / MemoryStore / DictMemory
    safety.py            # check_input / check_output / check_command / safe_path
    sandbox.py           # Sandbox（路径与命令隔离）
    verifier.py          # Verifier（死循环护栏）
    skill.py             # Skill / SkillRegistry / SkillProvider
    tools.py             # ToolResult / ToolRegistry + 已注册的本地 hand 工具
    utils.py             # base 内部辅助函数（glob 匹配、纯 Python diff 应用等）
  core/
    __init__.py          # public engine API
    aider_core.py        # AiderCore engine (wraps aider-chat)
    session.py           # SessionManager (caches per-session cores)
    config.py            # CoreConfig + default_config()
    executor.py          # bounded execution + error classification
    planner.py           # planning helper
    toolcall.py          # tool-result plumbing
    capture_io.py        # aider IO capture (pure, aider-only)
    prompts.py           # prompt templates (pure)
    inmemory.py          # in-memory port implementations + make_inmemory_core()
    subagent.py          # sub-agent execution helper
    ports.py / safety.py / sandbox.py / context.py / verifier.py  # 转发 shim，指向 base 对应模块（向后兼容旧导入路径）
  orchestration/
    __init__.py          # public orchestration API
    config.py            # OrchestrationConfig
    unit.py              # AgentInput / AgentOutput / AgentUnit / AiderAgentUnit
    registry.py          # AgentRegistry (role name -> AgentUnit)
    router.py            # Router / RouteDecision / KeywordRouter / LLMRouter
    pipeline.py          # Pipeline (sequential AgentUnit composition)
    team.py              # Team coordinator (LangGraph StateGraph) + TeamResult + build_default_team()
    visualization.py     # team_to_mermaid / export_mermaid (no LangGraph import)
```

## Quick start

### Single agent

`make_inmemory_core` wires the engine with trivial in-memory ports so it can
run without a Django project (tests, CLI experiments):

```python
from snowland_agent_core.core.inmemory import make_inmemory_core

core = make_inmemory_core("demo", workspace="/tmp/work")
result = core.chat("Refactor utils.py to use pathlib instead of os.path")
print(result["reply"])
print("edited:", result.get("edited_files"))
```

`AiderCore.chat(message, context=None, skills=None, tool_results=None)` returns
a `dict` with `reply`, `edited_files`, `output`, `safety_warnings`, and
gating flags (`refused` / `terminated`) when the safety gates fire.

### Custom LLM endpoint (OpenAI-compatible vendors)

AiderCore routes requests through litellm. For OpenAI-compatible vendors
(zhipu / deepseek / moonshot / qwen / hunyuan) the model id is passed as-is
and `custom_llm_provider` is pinned to `openai`; the real host is selected by
`api_base`. **Always pass `api_base`** — an empty `api_base` makes litellm fall
back to `https://api.openai.com/v1` and silently mis-route a zhipu call to
OpenAI. `make_core_kwargs()` (tests) and `make_inmemory_core()` forward
`api_base` to `AiderCore`.

```python
core = make_inmemory_core(
    "demo",
    provider="zhipu",
    model="glm-4.7-flash",
    api_base="https://open.bigmodel.cn/api/paas/v4/",
    api_key="<your-key>",
    workspace="/tmp/work",
)
```

### Multi-agent team

```python
from snowland_agent_core.core.inmemory import make_inmemory_core
from snowland_agent_core.orchestration import build_default_team

# build_default_team accepts a make_core(session_id, **kwargs) -> AiderCore factory.
team = build_default_team(make_inmemory_core)

result = team.run("Implement a retry decorator with exponential backoff")
print(result.reply)
print("trace:", [step.role for step in result.trace])
```

The default team registers `crafter`, `asker`, and `planner` units plus an
`implement` pipeline (`planner` -> `crafter`), and routes the task with a
`KeywordRouter`. Set `OrchestrationConfig(default_router="llm")` and pass a
supervisor `make_supervisor_core` factory to use an `LLMRouter` instead.

## Public API surface

Engine (`snowland_agent_core.core`):

- `AiderCore` — the single-agent engine.
- `SessionManager` — caches per-session `AiderCore` instances.
- `CoreConfig` / `default_config()` — engine configuration.
- `CredentialRepo`, `SessionRepo`, `InvocationLog` — injection ports.
- `make_inmemory_core()` / `make_inmemory_manager()` — in-memory wiring.

Orchestration (`snowland_agent_core.orchestration`):

- `AgentInput`, `AgentOutput`, `AgentUnit`, `AiderAgentUnit`.
- `AgentRegistry`, `Router`, `RouteDecision`, `KeywordRouter`, `LLMRouter`.
- `Pipeline`, `Team`, `TeamResult`, `TeamTraceStep`, `build_default_team()`.
- `OrchestrationConfig`, `team_to_mermaid()`, `export_mermaid()`.

## Development & testing

```bash
# Run the test suite (standard-library unittest only; no pytest required)
python -m unittest discover -s test -t .

# Build the distribution
python -m build
```

## License

BSD-3-Clause. See [`LICENSE`](LICENSE).
