> ## Documentation Index
> Fetch the complete documentation index at: https://docs.loom.teamecho.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Python API

> Use Loom programmatically in your Python applications.

## Basic Setup

```python theme={null}
import asyncio
from loom import Loom, LoomConfig
from loom.prompts import default_cm_prompt, DEFAULT_CHATBOT_PROMPT

config = LoomConfig.from_file()  # auto-resolves config path
```

## Using a Preset Template

The recommended way to get started:

```python theme={null}
loom = (
    Loom(config)
    .from_template("general")
    .cm_prompt(default_cm_prompt)
    .chatbot_prompt(DEFAULT_CHATBOT_PROMPT)
)
```

## Defining Schema Manually

For custom schema structures:

```python theme={null}
from loom import UniversalSchemaDomain

user = UniversalSchemaDomain("user_profile")
user.create_field("identity.name", "", "User's name")
user.create_field("preferences.hobbies", "", "Hobbies and interests")

loom = (
    Loom(config)
    .register(user)
    .cm_prompt(default_cm_prompt)
    .chatbot_prompt(DEFAULT_CHATBOT_PROMPT)
)
```

## Build Memory

Process any text into structured memory:

```python theme={null}
asyncio.run(loom.build("""
    [user]: I'm Bob, I love hiking and sushi.
    [assistant]: Great taste, Bob!
""", session_id="bob"))
```

## Chat with Memory

Generate responses grounded in schema data:

```python theme={null}
result = asyncio.run(loom.chat("What food do I like?", session_id="bob"))
print(result["reply"])  # "You mentioned that you love sushi!"
```

`chat()` returns a dict with the following keys:

| Key               | Type   | Description                                                  |
| ----------------- | ------ | ------------------------------------------------------------ |
| `reply`           | `str`  | The chatbot response                                         |
| `schema_updated`  | `bool` | Whether schema auto-update was triggered this round          |
| `context_rounds`  | `int`  | Number of chat rounds included as context                    |
| `total_rounds`    | `int`  | Total chat rounds in this session                            |
| `memory_injected` | `bool` | Whether schema memory was injected (context window exceeded) |
| `request_data`    | `dict` | The system prompt and messages sent to the LLM               |

## Listen Mode

`listen()` provides automatic periodic memory extraction — it accumulates chat history, runs the CM agent in build mode every N turns, and recalls schema data every turn:

```python theme={null}
config = LoomConfig.from_file()
config.build_every_n_turns = 3  # run build CM every 3 turns

loom = (
    Loom(config)
    .from_template("general")
    .cm_prompt(default_cm_prompt)
    .chatbot_prompt(DEFAULT_CHATBOT_PROMPT)
)

# listen() returns the chatbot reply string directly
reply = asyncio.run(loom.listen("I'm Bob, I love hiking.", session_id="bob"))
print(reply)
```

Unlike `chat()`, `listen()` returns a plain string (not a dict). Build triggers when `turn_number % build_every_n_turns == 0`.

## Selective Recall

`recall()` runs the CM agent in QA mode to retrieve only the schema fields relevant to a message:

```python theme={null}
result = asyncio.run(loom.recall("What food do I like?", session_id="bob"))
print(result["recalled"])      # Formatted schema excerpt
print(result["is_selective"])  # True if the CM agent found relevant data
```

## Dynamic Schema Update

Enable automatic schema updates during chat:

```python theme={null}
config = LoomConfig.from_file()
config.build_every_n_turns = 3  # update schema every 3 rounds

loom = (
    Loom(config)
    .from_template("general")
    .cm_prompt(default_cm_prompt)
    .chatbot_prompt(DEFAULT_CHATBOT_PROMPT)
)

# chat() returns a dict with reply and schema_updated flag
result = asyncio.run(loom.chat("I just got promoted to Senior Engineer!", session_id="bob"))
print(result["reply"])            # The chatbot response
print(result["schema_updated"])   # True if schema was auto-updated this round

# Manual trigger: update schema from last 10 rounds of chat
asyncio.run(loom.update_schema_from_chat(session_id="bob", rounds=10))
```

## Plugins & Custom Tools

Register plugins via `.use()` and custom SCP tools via `.tools()`:

```python theme={null}
loom = (
    Loom(config)
    .from_template("general")
    .cm_prompt(default_cm_prompt)
    .chatbot_prompt(DEFAULT_CHATBOT_PROMPT)
    .use(MyRAGPlugin("http://localhost:9000"))   # add plugin hooks
    .tools(my_custom_tools_builder)              # add custom SCP tools
)
```

See [Plugins](/extending/plugins) for the full plugin protocol.

## Config Loading

Multiple ways to load configuration:

```python theme={null}
from loom import LoomConfig

config = LoomConfig.from_file()                      # auto-resolve + env vars
config = LoomConfig.from_file("configs/loom.yaml")   # explicit path
config = LoomConfig.from_env()                        # env vars only
config = LoomConfig(model="gpt-5.4")                  # direct construction
```

## Custom CM Prompt

Override the default CM agent behavior:

```python theme={null}
def my_cm_prompt(state, registry):
    schema_overview = registry.inspect_all(max_depth=state.get("inspect_max_depth", 3))
    return f"""You are a medical records agent.

Available schemas:
{schema_overview}

Extract: diagnoses, medications, allergies, vitals.
When done: {{"continue": false}}"""

loom = Loom(config).cm_prompt(my_cm_prompt).chatbot_prompt(my_template)
```
