Metadata-Version: 2.4
Name: innerloop
Version: 0.0.0.dev2
Summary: LLM in a loop with tools, MCP, sessions, and structured outputs.
Project-URL: Homepage, https://github.com/botassembly/innerloop
Project-URL: Documentation, https://botassembly.org/innerloop
Project-URL: Repository, https://github.com/botassembly/innerloop
Project-URL: Issues, https://github.com/botassembly/innerloop/issues
Project-URL: Changelog, https://botassembly.org/innerloop/changelog/
Author-email: Ian Maurer <imaurer@gmail.com>
License-Expression: MIT
License-File: LICENSE
Keywords: agent,ai,anthropic,automation,claude,codex,devtool,gemini,llm,openai,sdk
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Software Development :: Code Generators
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: psutil>=7.1.2
Requires-Dist: pydantic>=2.12.0
Provides-Extra: dev
Requires-Dist: black>=23.7.0; extra == 'dev'
Requires-Dist: mkdocs-material>=9.5.0; extra == 'dev'
Requires-Dist: mkdocs>=1.6.0; extra == 'dev'
Requires-Dist: mkdocstrings[python]>=0.26.1; extra == 'dev'
Requires-Dist: mypy>=1.5.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
Requires-Dist: pytest-html>=4.1.1; extra == 'dev'
Requires-Dist: pytest-mock>=3.11.0; extra == 'dev'
Requires-Dist: pytest>=7.4.0; extra == 'dev'
Requires-Dist: ruff>=0.0.285; extra == 'dev'
Requires-Dist: types-psutil>=5.9.5.20240517; extra == 'dev'
Description-Content-Type: text/markdown

# InnerLoop

Python SDK for invoking the [OpenCode](https://opencode.ai/) coding CLI in a headless, non-interactive manner.

Supports:

- Synchronous and Asynchronous modes
- Structured outputs using Pydantic models
- Sessions for multiple invocations with shared context
- Permission configuration for reading, writing, web, and bash tools
- Configurable working directory per loop, session, or call

## Quick Start

See [docs/guides/installing-opencode.md](docs/guides/installing-opencode.md) for installation and configuration.

Quick install:
```bash
make install_opencode  # Handles bun config automatically
```

Or manually:
```bash
npm install -g opencode-ai
export OPENCODE_API_KEY=your-key
opencode models
```

## Installation

You can install `innerloop` using `uv` (recommended) or `pip`.

```bash
# Using uv
uv pip install innerloop

# Using pip
pip install innerloop
```

## Prerequisites

**InnerLoop requires the OpenCode CLI.** Install it and ensure `opencode` is on your PATH:

```bash
opencode --version
```

If the command fails, install OpenCode from [opencode.ai](https://opencode.ai/) and add it to your PATH.

See the [OpenCode documentation](https://opencode.ai/docs/) for installation instructions.

## Usage

<!-- BEGIN USAGE -->
We summarize results below each snippet without `print()` in the examples.
Summary shows: Output (first 100 chars), Duration (ms), Events.

To render yourself from a Response object:

```python
def show(resp):
    print('Output:', str(resp.output)[:100])
    dur = (resp.time.end - resp.time.start) if resp.time else 0
    print(f'Duration: {dur} ms')
    print(f'Events: {resp.event_count}')
    print(resp.model_dump_json(by_alias=True, indent=4))
```

See: src/innerloop/response.py


### Synchronous Run

```python
from innerloop import Loop

loop = Loop(model="anthropic/claude-haiku-4-5")
response = loop.run("Say hello, one short line.")
```

```text
Output: Hello! How can I help you with your code today?
Duration: 3203 ms
Events: 3
```

<details>
  <summary>JSON Output</summary>

```json
{
    "session_id": "ses_5c3c3ddbaffe1xdYfKWI7bvq3I",
    "input": "Say hello, one short line.",
    "output": "Hello! How can I help you with your code today?",
    "attempts": 1,
    "events": [
        {
            "seq": 1,
            "timestamp": 1761947166821,
            "type": "step_start"
        },
        {
            "seq": 2,
            "timestamp": 1761947166943,
            "type": "text",
            "text": "Hello! How can I help you with your code today?"
        },
        {
            "seq": 3,
            "timestamp": 1761947166991,
            "type": "step_finish"
        }
    ],
    "time": {
        "start": 1761947165017,
        "end": 1761947168220
    },
    "event_count": 3
}
```
</details>

### Asynchronous Run

```python
import asyncio
from innerloop import Loop

async def main():
    loop = Loop(model="anthropic/claude-haiku-4-5")
    async with loop.asession() as s:
        await s("Remember this number: 42")
        response = await s("What was the number?")

asyncio.run(main())
```

```text
Output: The number was 42.
Duration: 4165 ms
Events: 3
```

<details>
  <summary>JSON Output</summary>

```json
{
    "session_id": "ses_5c3c3d139ffeguE8rhOWW70ST2",
    "input": "What was the number?",
    "output": "The number was 42.",
    "attempts": 1,
    "events": [
        {
            "seq": 1,
            "timestamp": 1761947173793,
            "type": "step_start"
        },
        {
            "seq": 2,
            "timestamp": 1761947173971,
            "type": "text",
            "text": "The number was 42."
        },
        {
            "seq": 3,
            "timestamp": 1761947174039,
            "type": "step_finish"
        }
    ],
    "time": {
        "start": 1761947172214,
        "end": 1761947176379
    },
    "event_count": 3
}
```
</details>

### Tool Use (with workdir)

```python
from innerloop import Loop, allow

loop = Loop(
    model="anthropic/claude-haiku-4-5",
    perms=allow("bash"),
)
loop.default_workdir = "src/innerloop"
response = loop.run("Use bash: ls -1\nReturn only the raw command output.")
```

```text
Output: ```
__init__.py
__pycache__
api.py
config.py
errors.py
events.py
helper.py
invoke.py
mcp.py
output.…
Duration: 4802 ms
Events: 6
```

<details>
  <summary>JSON Output</summary>

```json
{
    "session_id": "ses_5c3c3b15cffecPu1g9yj32ee7j",
    "input": "Use bash: ls -1\nReturn only the raw command output.",
    "output": "```\n__init__.py\n__pycache__\napi.py\nconfig.py\nerrors.py\nevents.py\nhelper.py\ninvoke.py\nmcp.py\noutput.py\npermissions.py\nproc.py\nproviders.py\nrequest.py\nresponse.py\nstructured.py\nusage.py\n```",
    "attempts": 1,
    "events": [
        {
            "seq": 1,
            "timestamp": 1761947178427,
            "type": "step_start"
        },
        {
            "seq": 2,
            "timestamp": 1761947178973,
            "type": "tool_use",
            "output": "__init__.py\n__pycache__\napi.py\nconfig.py\nerrors.py\nevents.py\nhelper.py\ninvoke.py\nmcp.py\noutput.py\npe… (truncated)",
            "status": "completed",
            "tool": "bash"
        },
        {
            "seq": 3,
            "timestamp": 1761947179001,
            "type": "step_finish"
        },
        {
            "seq": 4,
            "timestamp": 1761947180024,
            "type": "step_start"
        },
        {
            "seq": 5,
            "timestamp": 1761947180306,
            "type": "text",
            "text": "```\n__init__.py\n__pycache__\napi.py\nconfig.py\nerrors.py\nevents.py\nhelper.py\ninvoke.py\nmcp.py\noutput.py\npermissions.py\nproc.py\nproviders.py\nrequest.py\nresponse.py\nstructured.py\nusage.py\n```"
        },
        {
            "seq": 6,
            "timestamp": 1761947180351,
            "type": "step_finish"
        }
    ],
    "time": {
        "start": 1761947176380,
        "end": 1761947181182
    },
    "event_count": 6
}
```
</details>

### Synchronous Session

```python
from innerloop import Loop

loop = Loop(model="anthropic/claude-haiku-4-5")
with loop.session() as s:
    s("Please remember this word for me: avocado")
    response = s("What was the word I asked you to remember?")
```

```text
Output: The word you asked me to remember was **avocado**.
Duration: 4236 ms
Events: 3
```

<details>
  <summary>JSON Output</summary>

```json
{
    "session_id": "ses_5c3c39e98ffeZy12Mg9NGfkPEY",
    "input": "What was the word I asked you to remember?",
    "output": "The word you asked me to remember was **avocado**.",
    "attempts": 1,
    "events": [
        {
            "seq": 1,
            "timestamp": 1761947187603,
            "type": "step_start"
        },
        {
            "seq": 2,
            "timestamp": 1761947187926,
            "type": "text",
            "text": "The word you asked me to remember was **avocado**."
        },
        {
            "seq": 3,
            "timestamp": 1761947187983,
            "type": "step_finish"
        }
    ],
    "time": {
        "start": 1761947185898,
        "end": 1761947190134
    },
    "event_count": 3
}
```
</details>

### Asynchronous Session

```python
import asyncio
from innerloop import Loop

async def main():
    loop = Loop(model="anthropic/claude-haiku-4-5")
    async with loop.asession() as s:
        await s("Remember this number: 42")
        response = await s("What was the number?")

asyncio.run(main())
```

```text
Output: The number you asked me to remember was **42**.
Duration: 3613 ms
Events: 3
```

<details>
  <summary>JSON Output</summary>

```json
{
    "session_id": "ses_5c3c37b9bffemhsiMcIp7Izjls",
    "input": "What was the number?",
    "output": "The number you asked me to remember was **42**.",
    "attempts": 1,
    "events": [
        {
            "seq": 1,
            "timestamp": 1761947195220,
            "type": "step_start"
        },
        {
            "seq": 2,
            "timestamp": 1761947195416,
            "type": "text",
            "text": "The number you asked me to remember was **42**."
        },
        {
            "seq": 3,
            "timestamp": 1761947195491,
            "type": "step_finish"
        }
    ],
    "time": {
        "start": 1761947193730,
        "end": 1761947197343
    },
    "event_count": 3
}
```
</details>

### Structured Output

```python
from innerloop import Loop, allow
from pydantic import BaseModel

class HNStory(BaseModel):
    title: str
    url: str
    points: int
    comments: int

class HNTop(BaseModel):
    stories: list[HNStory]

loop = Loop(
    model="anthropic/claude-haiku-4-5",
    perms=allow(webfetch=True),
)

prompt = (
    "Using web search, find the current top 5 stories on Hacker News.\n"
    "Prefer news.ycombinator.com (front page or item pages). For each,\n"
    "return: title, url, points (int), comments (int). Output JSON with\n"
    "a 'stories' array. If counts are missing, open the item page and\n"
    "extract them. Keep titles unmodified.\n"
)
response = loop.run(prompt, response_format=HNTop)
```

```text
Output: Futurelock: A subtle risk in async Rust — https://rfd.shared.oxide.computer/rfd/0609
Duration: 9950 ms
Events: 7
```

<details>
  <summary>JSON Output</summary>

```json
{
    "session_id": "ses_5c3c35f7dffetSWcljxI1M3XaO",
    "input": "Using web search, find the current top 5 stories on Hacker News.\nPrefer news.ycombinator.com (front page or item pages). For each,\nreturn: title, url, points (int), comments (int). Output JSON with\na 'stories' array. If counts are missing, open the item page and\nextract them. Keep titles unmodified.\n",
    "output": {
        "stories": [
            {
                "title": "Futurelock: A subtle risk in async Rust",
                "url": "https://rfd.shared.oxide.computer/rfd/0609",
                "points": 119,
                "comments": 40
            },
            {
                "title": "Introducing architecture variants",
                "url": "https://discourse.ubuntu.com/t/introducing-architecture-variants-amd64v3-now-available-in-ubuntu-25-10/71312",
                "points": 127,
                "comments": 95
            },
            {
                "title": "A theoretical way to circumvent Android developer verification",
                "url": "https://enaix.github.io/2025/10/30/developer-verification.html",
                "points": 20,
                "comments": 3
            },
            {
                "title": "Hacking India's largest automaker: Tata Motors",
                "url": "https://eaton-works.com/2025/10/28/tata-motors-hack/",
                "points": 75,
                "comments": 24
            },
            {
                "title": "Leaker reveals which Pixels are vulnerable to Cellebrite phone hacking",
                "url": "https://arstechnica.com/gadgets/2025/10/leaker-reveals-which-pixels-are-vulnerable-to-cellebrite-phone-hacking/",
                "points": 81,
                "comments": 32
            }
        ]
    },
    "attempts": 1,
    "events": [
        {
            "seq": 1,
            "timestamp": 1761947199808,
            "type": "step_start"
        },
        {
            "seq": 2,
            "timestamp": 1761947199992,
            "type": "text",
            "text": "I'll fetch the top stories from Hacker News for you."
        },
        {
            "seq": 3,
            "timestamp": 1761947200523,
            "type": "tool_use",
            "output": "<html lang=\"en\" op=\"news\"><head><meta name=\"referrer\" content=\"origin\"><meta name=\"viewport\" content… (truncated)",
            "status": "completed",
            "tool": "webfetch"
        },
        {
            "seq": 4,
            "timestamp": 1761947200550,
            "type": "step_finish"
        },
        {
            "seq": 5,
            "timestamp": 1761947201907,
            "type": "step_start"
        },
        {
            "seq": 6,
            "timestamp": 1761947205636,
            "type": "text",
            "text": "Perfect! I have the top 5 stories from Hacker News with their points and comment counts. Let me verify the data is complete:\n\n1. Futurelock: A subtle risk in async Rust - 119 points, 40 comments\n2. Introducing architecture variants - 127 points, 95 comments\n3. A theoretical way to circumvent Android developer verification - 20 points, 3 comments\n4. Hacking India's largest automaker: Tata Motors - 75 points, 24 comments\n5. Leaker reveals which Pixels are vulnerable to Cellebrite phone hacking - 81 points, 32 comments\n\n```json\n{\n  \"stories\": [\n    {\n      \"title\": \"Futurelock: A subtle risk in async Rust\",\n      \"url\": \"https://rfd.shared.oxide.computer/rfd/0609\",\n      \"points\": 119,\n      \"comments\": 40\n    },\n    {\n      \"title\": \"Introducing architecture variants\",\n      \"url\": \"https://discourse.ubuntu.com/t/introducing-architecture-variants-amd64v3-now-available-in-ubuntu-25-10/71312\",\n      \"points\": 127,\n      \"comments\": 95\n    },\n    {\n      \"title\": \"A theoretical way to circumvent Android developer verification\",\n      \"url\": \"https://enaix.github.io/2025/10/30/developer-verification.html\",\n      \"points\": 20,\n      \"comments\": 3\n    },\n    {\n      \"title\": \"Hacking India's largest automaker: Tata Motors\",\n      \"url\": \"https://eaton-works.com/2025/10/28/tata-motors-hack/\",\n      \"points\": 75,\n      \"comments\": 24\n    },\n    {\n      \"title\": \"Leaker reveals which Pixels are vulnerable to Cellebrite phone hacking\",\n      \"url\": \"https://arstechnica.com/gadgets/2025/10/leaker-reveals-which-pixels-are-vulnerable-to-cellebrite-phone-hacking/\",\n      \"points\": 81,\n      \"comments\": 32\n    }\n  ]\n}\n```"
        },
        {
            "seq": 7,
            "timestamp": 1761947205701,
            "type": "step_finish"
        }
    ],
    "time": {
        "start": 1761947197346,
        "end": 1761947207296
    },
    "event_count": 7
}
```
</details>

### Providers — LM Studio (local)

```python
from innerloop import Loop

loop = Loop(
    model="lmstudio/google/gemma-3n-e4b",
    providers={
        "lmstudio": {
            "options": {"baseURL": "http://127.0.0.1:1234/v1"}
        },
    },
)

response = loop.run("In one concise sentence, say something creative about coding.")
```

```text
Output: Coding is like sculpting with logic, chipping away at the unknown until a coherent form emerges.
Duration: 4947 ms
Events: 3
```

<details>
  <summary>JSON Output</summary>

```json
{
    "session_id": "ses_5c3c33896ffec5XSCBBCzHTROG",
    "input": "In one concise sentence, say something creative about coding.",
    "output": "Coding is like sculpting with logic, chipping away at the unknown until a coherent form emerges.",
    "attempts": 1,
    "events": [
        {
            "seq": 1,
            "timestamp": 1761947211112,
            "type": "step_start"
        },
        {
            "seq": 2,
            "timestamp": 1761947211448,
            "type": "text",
            "text": "Coding is like sculpting with logic, chipping away at the unknown until a coherent form emerges."
        },
        {
            "seq": 3,
            "timestamp": 1761947211479,
            "type": "step_finish"
        }
    ],
    "time": {
        "start": 1761947207297,
        "end": 1761947212244
    },
    "event_count": 3
}
```
</details>

### MCP — Remote server (Context7)

```python
from innerloop import Loop, mcp

loop = Loop(
    model="anthropic/claude-sonnet-4-5",
    mcp=mcp(context7="https://mcp.context7.com/mcp"),
)
prompt = (
    "Use the context7 MCP server to search for FastAPI's latest "
    "async database patterns. Summarize in 2-3 sentences."
)
response = loop.run(prompt)
```

```text
Output: Based on FastAPI's latest documentation, async database patterns primarily use **dependency injecti…
Duration: 15167 ms
Events: 10
```

<details>
  <summary>JSON Output</summary>

```json
{
    "session_id": "ses_5c3c32543ffe8L35fByCQbDmEb",
    "input": "Use the context7 MCP server to search for FastAPI's latest async database patterns. Summarize in 2-3 sentences.",
    "output": "Based on FastAPI's latest documentation, async database patterns primarily use **dependency injection with `yield`** to manage database sessions. You create an async function that yields a database session (e.g., SQLModel/SQLAlchemy Session), which FastAPI automatically opens before request processing and closes after the response is sent, ensuring proper resource cleanup. The pattern leverages `SessionDep = Annotated[Session, Depends(get_session)]` for cleaner type hints and uses `async with` context managers for startup/shutdown operations like creating tables on application startup.",
    "attempts": 1,
    "events": [
        {
            "seq": 1,
            "timestamp": 1761947214785,
            "type": "step_start"
        },
        {
            "seq": 2,
            "timestamp": 1761947215606,
            "type": "text",
            "text": "I'll search for FastAPI's latest async database patterns using the Context7 MCP server."
        },
        {
            "seq": 3,
            "timestamp": 1761947216155,
            "type": "tool_use",
            "output": "Available Libraries (top matches):\n\nEach result includes:\n- Library ID: Context7-compatible identifi… (truncated)",
            "status": "completed",
            "tool": "context7_resolve_library_id"
        },
        {
            "seq": 4,
            "timestamp": 1761947216186,
            "type": "step_finish"
        },
        {
            "seq": 5,
            "timestamp": 1761947218217,
            "type": "step_start"
        },
        {
            "seq": 6,
            "timestamp": 1761947219639,
            "type": "tool_use",
            "output": "### FastAPI Dependency with `yield` and `async with` (Python)\n\nSource: https://github.com/fastapi/fa… (truncated)",
            "status": "completed",
            "tool": "context7_get_library_docs"
        },
        {
            "seq": 7,
            "timestamp": 1761947219665,
            "type": "step_finish"
        },
        {
            "seq": 8,
            "timestamp": 1761947222237,
            "type": "step_start"
        },
        {
            "seq": 9,
            "timestamp": 1761947225651,
            "type": "text",
            "text": "Based on FastAPI's latest documentation, async database patterns primarily use **dependency injection with `yield`** to manage database sessions. You create an async function that yields a database session (e.g., SQLModel/SQLAlchemy Session), which FastAPI automatically opens before request processing and closes after the response is sent, ensuring proper resource cleanup. The pattern leverages `SessionDep = Annotated[Session, Depends(get_session)]` for cleaner type hints and uses `async with` context managers for startup/shutdown operations like creating tables on application startup."
        },
        {
            "seq": 10,
            "timestamp": 1761947225704,
            "type": "step_finish"
        }
    ],
    "time": {
        "start": 1761947212244,
        "end": 1761947227411
    },
    "event_count": 10
}
```
</details>

### MCP — Local server (BioMCP)

```python
from innerloop import Loop, mcp

loop = Loop(
    model="anthropic/claude-sonnet-4-5",
    mcp=mcp(biomcp="uvx --from biomcp-python biomcp run"),
)
prompt = (
    "Using ONLY the biomcp MCP server tools, look up the BRAF V600E mutation. "
    "List associated cancer types and targeted drug therapies in 2-3 sentences."
)
response = loop.run(prompt, timeout=60.0)
```

```text
Output: Based on the biomcp search results, **BRAF V600E mutation is associated with multiple cancer types …
Duration: 40244 ms
Events: 17
```

<details>
  <summary>JSON Output</summary>

```json
{
    "session_id": "ses_5c3c2ea04ffexW3ZKYVCf4tuJI",
    "input": "Using ONLY the biomcp MCP server tools, look up the BRAF V600E mutation. List associated cancer types and targeted drug therapies in 2-3 sentences.",
    "output": "Based on the biomcp search results, **BRAF V600E mutation is associated with multiple cancer types including melanoma, thyroid cancer, colorectal cancer, and high-grade glioma**. **Targeted therapies include BRAF inhibitors (dabrafenib, vemurafenib) often combined with MEK inhibitors (trametinib), which specifically target the mutated BRAF protein and downstream MAPK pathway signaling**. The literature shows these combination therapies are used across different BRAF V600E-positive malignancies, though resistance mechanisms remain a clinical challenge.",
    "attempts": 1,
    "events": [
        {
            "seq": 1,
            "timestamp": 1761947230522,
            "type": "step_start"
        },
        {
            "seq": 2,
            "timestamp": 1761947231190,
            "type": "text",
            "text": "I'll look up the BRAF V600E mutation using the biomcp tools."
        },
        {
            "seq": 3,
            "timestamp": 1761947234217,
            "type": "tool_use",
            "output": "{\n  \"domain\": \"thinking\",\n  \"result\": \"Added thought 1 to main sequence. Progress: 1/3 thoughts. Nex… (truncated)",
            "status": "completed",
            "tool": "biomcp_think"
        },
        {
            "seq": 4,
            "timestamp": 1761947234312,
            "type": "step_finish"
        },
        {
            "seq": 5,
            "timestamp": 1761947236128,
            "type": "step_start"
        },
        {
            "seq": 6,
            "timestamp": 1761947239225,
            "type": "tool_use",
            "output": "{\n  \"domain\": \"thinking\",\n  \"result\": \"Added thought 2 to main sequence. Progress: 2/3 thoughts. Nex… (truncated)",
            "status": "completed",
            "tool": "biomcp_think"
        },
        {
            "seq": 7,
            "timestamp": 1761947239254,
            "type": "step_finish"
        },
        {
            "seq": 8,
            "timestamp": 1761947241093,
            "type": "step_start"
        },
        {
            "seq": 9,
            "timestamp": 1761947255261,
            "type": "tool_use",
            "output": "# Record 1\nId: chr10:g.32800350C>A\nScore: 1.0\nChrom: 10\n\n## Cadd\nLicense: http://bit.ly/2TIuab9\nPhre… (truncated)",
            "status": "completed",
            "tool": "biomcp_variant_searcher"
        },
        {
            "seq": 10,
            "timestamp": 1761947255927,
            "type": "tool_use",
            "output": "\n### cBioPortal Summary for BRAF\n- **Mutation Frequency**: 0.0% (0 mutations in 0 samples)\n- **Studi… (truncated)",
            "status": "completed",
            "tool": "biomcp_article_searcher"
        },
        {
            "seq": 11,
            "timestamp": 1761947255957,
            "type": "step_finish"
        },
        {
            "seq": 12,
            "timestamp": 1761947257893,
            "type": "step_start"
        },
        {
            "seq": 13,
            "timestamp": 1761947261508,
            "type": "tool_use",
            "output": "{\n  \"domain\": \"thinking\",\n  \"result\": \"Added thought 3 to main sequence. Progress: 3/3 thoughts. Thi… (truncated)",
            "status": "completed",
            "tool": "biomcp_think"
        },
        {
            "seq": 14,
            "timestamp": 1761947261558,
            "type": "step_finish"
        },
        {
            "seq": 15,
            "timestamp": 1761947263073,
            "type": "step_start"
        },
        {
            "seq": 16,
            "timestamp": 1761947266114,
            "type": "text",
            "text": "Based on the biomcp search results, **BRAF V600E mutation is associated with multiple cancer types including melanoma, thyroid cancer, colorectal cancer, and high-grade glioma**. **Targeted therapies include BRAF inhibitors (dabrafenib, vemurafenib) often combined with MEK inhibitors (trametinib), which specifically target the mutated BRAF protein and downstream MAPK pathway signaling**. The literature shows these combination therapies are used across different BRAF V600E-positive malignancies, though resistance mechanisms remain a clinical challenge."
        },
        {
            "seq": 17,
            "timestamp": 1761947266162,
            "type": "step_finish"
        }
    ],
    "time": {
        "start": 1761947227411,
        "end": 1761947267655
    },
    "event_count": 17
}
```
</details>
<!-- END USAGE -->
