Metadata-Version: 2.4
Name: dispersl-sdk
Version: 0.1.6
Summary: Production Python SDK for Dispersl API
Requires-Python: >=3.9
Requires-Dist: cryptography>=43.0.0
Requires-Dist: eval-type-backport>=0.2.0
Requires-Dist: httpx>=0.27.2
Requires-Dist: pydantic>=2.9.2
Provides-Extra: dev
Requires-Dist: build>=1.2.2; extra == 'dev'
Requires-Dist: mypy>=1.13.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.24.0; extra == 'dev'
Requires-Dist: pytest>=8.3.3; extra == 'dev'
Requires-Dist: ruff>=0.7.2; extra == 'dev'
Description-Content-Type: text/markdown

<p align="center">
 <img width="300px" src="https://github.com/Code-Fundi/.github/blob/main/media/dispersl/banner-light.png?raw=true" align="center" alt="Dispersl Multi-Agent SDK" />
</p>
</p>

<p align="center">
  <a href="https://discord.gg/6RJTWCuWZj">
    <img src="https://img.shields.io/badge/Discord-7289DA?logo=discord&logoColor=white" />
  </a>
  <a href="https://x.com/disperslHQ">
    <img src="https://img.shields.io/badge/X/Twitter-808080?logo=x&logoColor=white" />
  </a>
  <a href="https://www.tiktok.com/@codefundi">
    <img src="https://img.shields.io/badge/TikTok-000000?logo=tiktok&logoColor=white" />
  </a>
  <a href="https://dispersl.com">
    <img src="https://img.shields.io/badge/Website-dispersl.com-blue" />
  </a>
<br />
</p>

#

<h2 align="center">Dispersl Python SDK</h2>
<p align="center">Flexible workflow automation with plug-and-play agents for Python. Behavioral feature parity with <code>@codefundi/dispersl-sdk</code> (TypeScript).</p>

## Install

```bash
pip install dispersl-sdk
```

## Requirements

- Python `>=3.9`
- Async runtime (`asyncio`)

## Quick Start

```python
import asyncio

from dispersl import AgenticExecutor, AsyncDisperslClient


async def main() -> None:
    client = AsyncDisperslClient(
        base_url="https://api.dispersl.com/v1",
        api_key="YOUR_API_KEY",
        timeout_s=120.0,
        retry_attempts=3,
    )

    executor = AgenticExecutor(client)
    out = await executor.run_plan_and_agent_loop(
        prompt="Plan and implement a production webhook pipeline",
        agent_choices="auto",  # or ["architect", "security-auditor", "release-manager"]
        execution_sequence="sequential",  # or "parallel" for concurrent agent execution
    )

    print(out["task_id"], len(out["events"]), len(out["tool_results"]))
    await client.aclose()


asyncio.run(main())
```

## SDK Capabilities

- Async HTTP client with bearer auth, retries, timeout support, and structured error mapping.
- Optional `codefundi_api_key` → `x-codefundi-key` metering header; session state headers (`x-dispersl-state`, `x-dispersl-turn-seq`).
- Full endpoint coverage for `agent/completion`, `agent/plan`, and agent lifecycle APIs.
- Incremental NDJSON stream parser (`NdjsonParser`) with split-buffer handling and parse errors.
- NDJSON chunk normalization (inline `tool_calls` in content, top-level `tool_calls` → `tools`) via `StreamContentNormalizer`.
- Stream turn parsing (`parse_agent_stream`): one API stream → N local tool executions → grouped results.
- Stateful grouped tool feedback (`build_grouped_tool_feedback_prompt`) — no previous-assignment echo.
- Loop detection (soft/hard thresholds, ping-pong, result-aware signatures).
- Handover parser supporting nested and double-serialized tool arguments; control tools not executed locally.
- Sequential and parallel agent execution modes for flexible workflow orchestration.
- Task continuation support via `task_id` for multi-phase workflows.
- MCP config loading from `.dispersl/mcp.json` with env interpolation, validation, catalog search/describe meta tools.
- Agentic execution loop with plan-to-agent transitions, tool execution, and end-session detection (`max_loops` default **25**).

## Client API Surface

### Agent execution endpoints

| Method | Request | Endpoint |
| --- | --- | --- |
| `agent_completion` | `dict[str, Any]` | `POST /agent/completion` |
| `agent_plan` | `dict[str, Any]` | `POST /agent/plan` |

### Agent plan choices

`agent_plan` accepts:

- `"auto"` for automatic custom-agent selection
- `list[str]` for explicit custom agent `name_id` values

When `"auto"` is passed, the SDK normalizes the request payload to `["auto"]` for API compatibility.

### Execution modes

`agent_plan` and `run_plan_and_agent_loop` support execution sequence control:

- `"sequential"` (default): agents execute one after another
- `"parallel"`: multiple agents execute concurrently when handed over from plan

For parallel execution, use `parallel_concurrency` to limit simultaneous agent runs.

### Resource endpoints

| Domain | Method | Endpoint |
| --- | --- | --- |
| Agents | `agents(limit=20, next_token=None)` | `GET /agents?limit&nextToken` |
| Agents | `agents_create(body)` | `POST /agents/create` |
| Agents | `agents_edit(agent_id, body)` | `POST /agents/edit/{id}` |
| Agents | `agent_by_id(agent_id)` | `GET /agents/{agent_id}` |
| Agents | `agent_delete(agent_id)` | `DELETE /agents/{agent_id}` |

## Execution Loop Behavior

`AgenticExecutor.run_plan_and_agent_loop` includes:

- plan-first execution and automatic transition to selected specialist agent
- execution sequence control (`execution_sequence`: `"sequential"` or `"parallel"`)
- parallel concurrency limit (`parallel_concurrency`)
- handover propagation; control tools (`end_session`, `finish_task`, `handover_task`) are **not** passed to `tool_executor`
- stateful grouped continuation prompts via `tool_feedback` (no turn-text / previous-assignment echo)
- soft loop → `extra_directive`; hard loop → synthetic error chunk + stop
- optional custom tool runner (`tool_executor`)
- deterministic termination with `max_loops` (default **25**)
- returned payload: `task_id`, `events`, `tool_results`, `execution_sequence`, `workflow_complete`

### Direct Single-Agent Completion Loop

```python
executor = AgenticExecutor(client)
result = await executor.run_agent_completion_loop(
    name_id="architect",
    prompt="Review this backend design and produce a migration plan",
    max_loops=25,
)
```

### Task Continuation

```python
first_run = await executor.run_plan_and_agent_loop(
    prompt="Design the system architecture",
    agent_choices="auto",
)

result = await executor.run_plan_and_agent_loop(
    prompt="Now implement the core modules",
    agent_choices="auto",
    task_id=first_run["task_id"],
)
```

## Core Models and Helpers

| Component | Purpose |
| --- | --- |
| `DisperslConfig` | client init (`base_url`, `api_key`, `codefundi_api_key`, timeout, retries) |
| `NDJSONChunk` | typed stream chunk (`state`, `knowledge`, `audio`, metadata) |
| `ToolCall` / `ToolResult` | tool invocation + local result (`tool_call_id`) |
| `parse_agent_stream` | collect tools from one stream, execute batch, return `StreamTurnResult` |
| `build_grouped_tool_feedback_prompt` | format N tool results into one continuation prompt |
| `LoopDetector` | soft/hard loop detection |
| `MCPConfigLoader` / `MCPRegistry` | MCP config + runtime tool registration |
| `validate_mcp_payload_before_send` | client-side MCP limits / reserved names |

## Security / Session State Env Vars

| Variable | Purpose |
| --- | --- |
| `DISPERSL_STATE_PUBLIC_KEY` | PEM public key for verifying signed `chunk.state` |
| `DISPERSL_STATE_SIGNING_REQUIRED` | When truthy, invalid/missing state raises `StreamSecurityError` |

## Error Model

| Error | Trigger |
| --- | --- |
| `AuthenticationError` | auth failures |
| `NotFoundError` | `404` |
| `ConflictError` | `409` |
| `RateLimitError` | `429` |
| `InsufficientCreditsError` | `402` metering |
| `ValidationError` | invalid request payload |
| `ServerError` | upstream `5xx` |
| `RequestTimeoutError` | timeout (`TimeoutError` alias for TS parity) |
| `StreamParseError` | invalid stream payload |
| `ToolExecutionError` | tool callback failed |
| `HandoverError` | malformed handover contract |
| `StreamSecurityError` | invalid signed session state |

## Development

```bash
python -m pip install -e ".[dev]"
python -m ruff check src tests
python -m ruff format --check src tests
python -m mypy src
python -m pytest -q
python -m build
```

## Example Quickstarts

End-to-end quickstarts live in root `examples/py`:

- `examples/py/plan_handover_loop.py`
- `examples/py/single_agent_completion.py`
- `examples/py/task_insight_progress.py`
- `examples/py/agent_lifecycle_and_stats.py`
- `examples/py/mcp_custom_agent_flow.py`

## Release

- Package name: `dispersl-sdk`
- Version: `0.1.6`
- Python release workflow: `.github/workflows/release-python.yml`
- Trigger: push tag `py-v*`
