{"id":32407556,"url":"https://github.com/timbal-ai/timbal","last_synced_at":"2026-05-10T12:13:05.508Z","repository":{"id":278774892,"uuid":"936558637","full_name":"timbal-ai/timbal","owner":"timbal-ai","description":"Timbal is an open-source python framework for building reliable AI applications, battle-tested in production with simple, transparent architecture that eliminates complexity while delivering blazing fast performance, robust typing, and API stability in an ever-changing ecosystem.","archived":false,"fork":false,"pushed_at":"2026-02-16T17:07:11.000Z","size":3854,"stargazers_count":38,"open_issues_count":0,"forks_count":8,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-02-17T00:27:44.065Z","etag":null,"topics":["agents","ai","ai-agents","ai-agents-framework","framework","llms"],"latest_commit_sha":null,"homepage":"https://timbal.ai","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/timbal-ai.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-02-21T09:42:42.000Z","updated_at":"2026-02-16T17:06:01.000Z","dependencies_parsed_at":"2025-02-21T16:40:23.729Z","dependency_job_id":"346dda08-006b-4c78-ab81-c8b51fc2cb86","html_url":"https://github.com/timbal-ai/timbal","commit_stats":null,"previous_names":["timbal-ai/timbal"],"tags_count":124,"template":false,"template_full_name":null,"purl":"pkg:github/timbal-ai/timbal","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timbal-ai%2Ftimbal","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timbal-ai%2Ftimbal/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timbal-ai%2Ftimbal/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timbal-ai%2Ftimbal/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/timbal-ai","download_url":"https://codeload.github.com/timbal-ai/timbal/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timbal-ai%2Ftimbal/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29575373,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-18T08:38:15.585Z","status":"ssl_error","status_checked_at":"2026-02-18T08:38:14.917Z","response_time":162,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agents","ai","ai-agents","ai-agents-framework","framework","llms"],"created_at":"2025-10-25T13:20:28.116Z","updated_at":"2026-05-10T12:13:05.489Z","avatar_url":"https://github.com/timbal-ai.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Timbal\n\n\u003e **Timbal 2.0 is in beta.** The API is stable — we're finalizing docs and tooling before the full release.\n\nSimple, performant, battle-tested framework for building reliable AI applications.\n\nFull documentation: [docs.timbal.ai](https://docs.timbal.ai)\n\n---\n\n## Installation\n\n```bash\npip install timbal\n```\n\nTimbal is modular. The bare install includes the agent/workflow engine, both Anthropic and OpenAI providers, MCP support, and tracing. Install extras only when you need them:\n\n| Extra | What it adds | When to use |\n|---|---|---|\n| `timbal[server]` | FastAPI + uvicorn | Serving agents over HTTP |\n| `timbal[documents]` | PyMuPDF + openpyxl + python-docx | Reading PDFs, Excel, Word files |\n| `timbal[evals]` | rich | Running the evals CLI |\n| `timbal[codegen]` | libcst + ruff | Using the code generation tools |\n| `timbal[all]` | Everything above | |\n\n```bash\npip install 'timbal[server]'\npip install 'timbal[documents,evals]'\npip install 'timbal[all]'\n```\n\n### From source\n\n```bash\ngit clone https://github.com/timbal-ai/timbal.git\ncd timbal\nuv sync --dev\n```\n\n---\n\n## Two patterns, one interface\n\n### Agent — autonomous reasoning\n\nThe LLM decides what to do. You provide tools and a goal.\n\n```python\nfrom timbal import Agent\nfrom timbal.tools import WebSearch\nfrom datetime import datetime\n\ndef get_datetime() -\u003e str:\n    return datetime.now().isoformat()\n\nagent = Agent(\n    name=\"assistant\",\n    model=\"anthropic/claude-sonnet-4-6\",\n    tools=[get_datetime, WebSearch()],\n    max_tokens=1024,\n)\n\nresult = await agent.collect(prompt=\"What time is it in Tokyo right now?\")\nprint(result.output)\n```\n\n### Workflow — explicit pipelines\n\nYou define the steps. The framework handles concurrency and dependency resolution.\n\n```python\nimport httpx\nfrom timbal import Workflow\nfrom timbal.state import get_run_context\nfrom timbal.tools import Write\n\nasync def fetch(url: str) -\u003e str:\n    async with httpx.AsyncClient(follow_redirects=True) as client:\n        return (await client.get(url)).text\n\nworkflow = (\n    Workflow(name=\"scraper\")\n    .step(fetch)\n    .step(\n        Write(),\n        path=\"./output.html\",\n        content=lambda: get_run_context().step_span(\"fetch\").output,\n    )\n)\n\nawait workflow.collect(url=\"https://timbal.ai\")\n```\n\nIndependent steps run concurrently. Dependencies are inferred automatically from `step_span()` references — no manual wiring needed.\n\n---\n\n## Calling runnables\n\nAll `Agent`, `Workflow`, and `Tool` instances share the same interface.\n\n```python\n# Collect all events, return final OutputEvent\nresult = await agent.collect(prompt=\"Hello\")\nprint(result.output)           # final result\nprint(result.status.code)      # \"success\" | \"error\" | \"cancelled\"\nprint(result.usage)            # {\"anthropic/claude-sonnet-4-6:input_tokens\": 42, ...}\n\n# Or stream events\nasync for event in agent(prompt=\"Hello\"):\n    if isinstance(event, DeltaEvent) and isinstance(event.item, TextDelta):\n        print(event.item.text_delta, end=\"\", flush=True)\n```\n\n---\n\n## Models\n\nAny provider, one interface. Model strings follow `provider/model-name`:\n\n```\nanthropic/claude-sonnet-4-6       openai/gpt-4o\nanthropic/claude-opus-4-7         openai/gpt-5.5\nanthropic/claude-opus-4-6         openai/gpt-5.5-2026-04-23\nanthropic/claude-haiku-4-5        openai/o3\ngoogle/gemini-2.5-flash           togetherai/deepseek-ai/DeepSeek-V4-Pro\ngoogle/gemini-2.5-pro-preview     togetherai/moonshotai/Kimi-K2.6\ngroq/llama-3.3-70b-versatile      xai/grok-4\ncerebras/llama-3.1-8b             sambanova/Meta-Llama-3.3-70B-Instruct\n```\n\nRecent additions (see `python/timbal/models.yaml`): DeepSeek V4 via Together/Fireworks, GLM-5.1, MiniMax M2.7, Kimi K2.6, Qwen 3.6 Plus (Fireworks), and the `gpt-5.5-2026-04-23` snapshot.\n\nFull list and context window sizes in `python/timbal/core/models.py`.\n\n---\n\n## Structured output\n\n```python\nfrom pydantic import BaseModel\n\nclass Analysis(BaseModel):\n    sentiment: str\n    confidence: float\n    summary: str\n\nagent = Agent(model=\"openai/gpt-4o-mini\", output_model=Analysis)\nresult = await agent.collect(prompt=\"Analyse: 'Timbal makes AI easy'\")\nprint(result.output.sentiment)     # \"positive\"\nprint(result.output.confidence)    # 0.97\n```\n\n---\n\n## Streaming\n\n```python\nfrom timbal.types.events import DeltaEvent\nfrom timbal.types.events.delta import TextDelta\n\nasync for event in agent(prompt=\"Write a short poem\"):\n    if isinstance(event, DeltaEvent) and isinstance(event.item, TextDelta):\n        print(event.item.text_delta, end=\"\", flush=True)\n```\n\n---\n\n## Memory compaction\n\nLong conversations grow large. Timbal has built-in strategies to keep context under control.\n\n```python\nfrom timbal.core.memory_compaction import (\n    compact_tool_results,\n    keep_last_n_messages,\n    keep_last_n_turns,\n    summarize,\n)\n\nagent = Agent(\n    model=\"anthropic/claude-sonnet-4-6\",\n    max_tokens=2048,\n    memory_compaction=[\n        compact_tool_results(keep_last_n=2),   # compress old tool outputs\n        keep_last_n_turns(10),                 # keep last 10 user↔assistant turns\n    ],\n    memory_compaction_ratio=0.75,              # trigger at 75% context window usage\n)\n```\n\n**Strategies:**\n- `compact_tool_results(keep_last_n, threshold, replacement)` — strips old tool results, optionally replacing with a summary string\n- `keep_last_n_messages(n)` — hard truncation, structure-aware (no orphaned tool pairs)\n- `keep_last_n_turns(n)` — keep last N user+assistant pairs\n- `summarize(threshold, model, keep_last_n, max_summary_tokens)` — async LLM-based summarization of old messages\n\nStrategies are applied in order. Multiple strategies can be combined.\n\n---\n\n## Skills\n\nSkills are reusable, self-documenting tool packages. They sit on disk and are loaded into the agent's context only when the LLM explicitly requests them via the auto-injected `read_skill` tool.\n\n```\nskills/\n└── web_research/\n    ├── SKILL.md          # frontmatter + docs shown to the LLM\n    └── tools/\n        ├── search.py\n        └── scrape.py\n```\n\n```yaml\n# SKILL.md\n---\nname: \"web_research\"\ndescription: \"Search the web and scrape pages\"\n---\nUse `search(query)` to find pages, then `scrape(url)` to get the content.\n```\n\n```python\nagent = Agent(\n    model=\"anthropic/claude-sonnet-4-6\",\n    skills_path=\"./skills\",\n    max_tokens=2048,\n)\n```\n\nThe agent sees skill names and descriptions at startup. It calls `read_skill(\"web_research\")` to load the tools and documentation when needed — keeping context clean until the skill is actually required.\n\n---\n\n## MCP servers\n\nConnect agents to any [Model Context Protocol](https://modelcontextprotocol.io) server.\n\n```python\nfrom timbal.core import MCPServer\n\n# Local server via stdio\nmcp = MCPServer(\n    transport=\"stdio\",\n    command=\"npx\",\n    args=[\"-y\", \"@modelcontextprotocol/server-filesystem\", \".\"],\n)\n\n# Remote server via HTTP\nmcp = MCPServer(\n    transport=\"http\",\n    url=\"https://my-mcp-server.com\",\n    headers={\"Authorization\": \"Bearer token\"},\n)\n\nagent = Agent(\n    model=\"anthropic/claude-sonnet-4-6\",\n    tools=[mcp],\n    max_tokens=2048,\n)\n```\n\n---\n\n## Conditional workflows\n\n```python\nworkflow = (\n    Workflow(name=\"pipeline\")\n    .step(validate_input)\n    .step(\n        process,\n        when=lambda: get_run_context().step_span(\"validate_input\").output[\"valid\"],\n        data=lambda: get_run_context().step_span(\"validate_input\").output[\"data\"],\n    )\n    .step(\n        notify_failure,\n        when=lambda: not get_run_context().step_span(\"validate_input\").output[\"valid\"],\n    )\n)\n```\n\nSteps with `when=` are skipped (not failed) when the condition is False. Downstream steps that depend on a skipped step are also skipped automatically.\n\n---\n\n## Observability\n\nTimbal has a layered tracing system. Every run produces a full span trace.\n\n```python\nfrom timbal.state.tracing.providers import JsonlTracingProvider\nfrom timbal.state.tracing.exporters import OTelExporter\nfrom pathlib import Path\n\nprovider = JsonlTracingProvider.configured(\n    _path=Path(\"traces.jsonl\"),\n    _exporters=[\n        OTelExporter(\n            endpoint=\"http://localhost:4318\",\n            service_name=\"my-agent\",\n            headers={\"x-honeycomb-team\": \"YOUR_KEY\"},\n        ),\n    ],\n)\n\nagent = Agent(model=\"...\", tracing_provider=provider)\n```\n\n`OTelExporter` is fire-and-forget — it never adds latency to your runs. Compatible with Jaeger, Honeycomb, Datadog, Grafana Tempo, and any OTLP backend. Custom exporters:\n\n```python\nfrom timbal.state.tracing.providers.base import Exporter\n\nclass MyExporter(Exporter):\n    async def export(self, run_context) -\u003e None:\n        spans = list(run_context._trace.values())\n        await my_backend.send(spans)\n```\n\n---\n\n## Session chaining\n\nLink runs so an agent can recall what happened in a previous session — even across process restarts.\n\n```python\nfrom timbal.state.tracing.providers import JsonlTracingProvider\nfrom pathlib import Path\n\nprovider = JsonlTracingProvider.configured(_path=Path(\"sessions.jsonl\"))\nagent = Agent(model=\"...\", tracing_provider=provider)\n\nrun1 = await agent.collect(prompt=\"My name is Alice.\")\nprint(run1.run_id)   # \"abc123\"\n\n# Next session — agent remembers the previous one\nfrom timbal.state.context import RunContext\nctx = RunContext(parent_id=\"abc123\", tracing_provider=provider)\nrun2 = await agent.collect(prompt=\"What's my name?\", run_context=ctx)\n```\n\n---\n\n## HTTP serving\n\nRequires `pip install 'timbal[server]'`. Serve any agent or workflow over HTTP with one command.\n\n```bash\npython -m timbal.server.http \\\n  --import_spec path/to/agent.py::my_agent \\\n  --host 0.0.0.0 \\\n  --port 4444 \\\n  --workers 4\n```\n\n| Endpoint | Method | Description |\n|---|---|---|\n| `/healthcheck` | GET | Returns 204 |\n| `/params_model_schema` | GET | JSON schema of inputs |\n| `/return_model_schema` | GET | JSON schema of output |\n| `/run` | POST | Execute and wait |\n| `/stream` | POST | Stream events as SSE |\n| `/cancel/{run_id}` | POST | Cancel a running execution |\n\n```bash\ncurl -X POST http://localhost:4444/run \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"prompt\": \"Hello\"}'\n```\n\n---\n\n## Evals\n\nDeclarative evaluation suite with built-in validators.\n\n```yaml\n# evals.yaml\nevals:\n  - name: \"greets_user\"\n    params:\n      prompt: \"Say hello to Alice\"\n    agent!:\n      output!:\n        contains_all!: [\"Hello\", \"Alice\"]\n      duration!:\n        lt!: 5\n```\n\n```python\nfrom timbal.evals.runner import run_eval\n\nresult = await run_eval(eval_config, agent=my_agent)\nprint(result.passed)\nprint(result.validator_results)\n```\n\n**Validators:** `contains`, `contains_all`, `contains_any`, `starts_with`, `ends_with`, `pattern`, `length`, `min_length`, `max_length`, `eq`, `lt`, `gt`, `type`, `email`, `json`, `semantic` (LLM-based), `language`. All support `not_` negation.\n\n---\n\n## Testing without API calls\n\n```python\nfrom timbal.core.test_model import TestModel\n\nmodel = TestModel(responses=[\"The answer is 42.\"])\nagent = Agent(name=\"test\", model=model, tools=[])\n\nresult = await agent.collect(prompt=\"What is the answer?\")\nassert result.output.collect_text() == \"The answer is 42.\"\nassert result.status.code == \"success\"\n```\n\nResponses cycle to the last item when exhausted. Pass `Message` objects to test tool-calling flows. No network calls.\n\n---\n\n## Hooks\n\nPre/post hooks run around every execution and have access to the full run context.\n\n```python\ndef log_pre():\n    ctx = get_run_context()\n    print(f\"Starting run {ctx.id}\")\n\ndef log_post():\n    span = get_run_context().current_span()\n    print(f\"Output: {span.output}\")\n\ntool = Tool(handler=my_fn, pre_hook=log_pre, post_hook=log_post)\n```\n\nHooks are parameterless callables. Both sync and async are supported.\n\n---\n\n## Running tests\n\n```bash\nuv run pytest\n```\n\n```bash\nuv run pytest python/tests/core/test_jsonl_tracing_provider.py\nuv run pytest python/tests/core/test_otel_exporter.py::TestRetry\n```\n\n---\n\n## Benchmarks\n\n```bash\ncd benchmarks/langchain\nuv pip install langchain-core langsmith langgraph\n\n# Quick mode (default)\nuv run pytest bench_*.py -v\n\n# Full mode\nTIMBAL_BENCH_MODE=full uv run pytest bench_*.py -v\n```\n\nSee [`benchmarks/README.md`](benchmarks/README.md) for methodology and how to read results.\n\n---\n\n## Repository structure\n\n```\ntimbal/\n├── python/\n│   ├── timbal/\n│   │   ├── core/             # Agent, Workflow, Tool, LLM router, Skills, MCP\n│   │   ├── state/            # RunContext, tracing providers + exporters\n│   │   ├── types/            # Message, File, Events\n│   │   ├── collectors/       # Output processing\n│   │   ├── evals/            # Evaluation framework\n│   │   ├── server/           # HTTP serving\n│   │   ├── platform/         # Timbal platform integration\n│   │   └── tools/            # Built-in tool library\n│   └── tests/core/\n├── benchmarks/\n│   ├── README.md\n│   └── langchain/\n├── CLAUDE.md                 # Codebase guide for AI agents\n└── pyproject.toml\n```\n\n---\n\n## Why Timbal\n\n**Transparent by default.** No hidden magic. Under the hood it's async functions, Pydantic validation, and event-driven streaming — nothing you couldn't build yourself, just already built well.\n\n**Production-shaped.** The core abstractions were refined through real production deployments before the framework was open-sourced. Fast failure, clear error messages, stable interfaces.\n\n**One interface for everything.** Agents, workflows, and tools all share the same `__call__` / `.collect()` convention and the same event stream. Compose them freely.\n\n**Provider-agnostic.** Anthropic, OpenAI, Google, Groq, xAI, Cerebras, SambaNova — same code, swap the model string.\n\n---\n\n## Documentation\n\n[docs.timbal.ai](https://docs.timbal.ai)\n\n## Contributing\n\nPull requests and issues welcome.\n\n## License\n\nApache 2.0 — see [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftimbal-ai%2Ftimbal","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftimbal-ai%2Ftimbal","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftimbal-ai%2Ftimbal/lists"}