{"owner":"emcie-co","repo":"parlant","hasSkills":true,"totalSkillsCount":5,"totalTokensCount":4531,"categories":["claude-rule","root-instruction","mcp-config","marketplace","plugin-manifest"],"hasMcp":true,"mcpConfig":{"mcpServers":{"parlant":{"command":"npx","args":["-y","@modelcontextprotocol/server-parlant"]}}},"found":["CLAUDE.md","llms.txt",".cursor/mcp.json",".claude-plugin/marketplace.json",".claude-plugin/plugin.json"],"skills":{"CLAUDE.md":"This is the main repo of Parlant (https://parlant.io).\n\nParlant is a Python based agent framework. Its core strengths:\n\n1. It allows you to create compliant and controlled AI agents for customer-facing use cases\n2. It provides many conversational management features out of the box\n3. It's built for enterprise, large-scale use cases, where SLAs, stability and security are paramount\n\nThe repo's structure follows the Hexagonal Architecture (Ports and Adapters) approach.\n\n- src/parlant\n  - core: Core framework code\n  - adapters: Implementations of interfaces using 3rd party tools\n  - api: REST API layer using FastAPI. Uses modules from core/\n- tests: all tests for the project. Structure strives to mirror that which is under src/parlant.\n\nGeneral Coding Instructions:\n\n- Always ensure you stick to Hexagonal Architecture patterns in line with how they're used in this codebase.\n- Every time you add something, look for similar things in the codebase and ensure you follow the coding style.\n- We use MyPy on strict mode. Every parameter needs to be type-annotated. Every function's result too.\n- If you need to add a test for something, first say where you plan to add it and ask for confirmation.\n- We follow TDD. When you make a change, first create a failing test. Once it fails, implement just enough so it passes.\n- If you need to test classes/methods in sdk.py (or generally to test things that relate to engine behavior) make sure you inherit from SDKTest and understand how it works and how to use it.\n- Test names should go \"test*that*...\" using clear names that explain the context, what is executed, and what is the expected result.\n- You can run tests using pytest. Make sure you run \"uv run pytest tests/path/to/test/file.py\" while also specifying the test name that you need to run.\n\nAlways follow this plan when asked to code a feature or fix a bug:\n\n1. Consider the codebase's structure\n2. Describe your implementation plan, including:\n   a. What tests you will write (test names + files they would live in)\n   b. Why do you think the tests would initially fail\n   c. Where you would plan to implement the code that would make the tests pass\n3. Ask for plan confirmation. If you get feedback, revise your plan and ask for confirmation again until you get it.\n4. Implement the tests first. Ask for confirmation and code review.\n5. Once tests are approved, once again suggest your implementation plan for making them pass, and get plan review until confirmation.\n6. Once your implementation plan is confirmed, go ahead with implementing the code to pass them.\n7. Make sure to format all of the files you changed using ruff (it is installed in the environment).\n8. Run `uv run python scripts/lint.py --mypy --ruff` to ensure your code has no lint issues.\n","llms.txt":"# Parlant\n\n> Open-source AI agent framework for building customer-facing conversational agents with ensured rule compliance and enterprise-grade behavior control.\n\nParlant is a Python framework for building **predictable, business-aligned AI agents**. Unlike prompt-based approaches, Parlant ensures agents follow behavioral rules through structured guideline matching and contextual application.\n\nInstall: `pip install parlant`\n\n## Quick Start Example\n\n```python\nimport parlant.sdk as p\nimport asyncio\n\n@p.tool\nasync def get_account_balance(context: p.ToolContext, account_id: str) -> p.ToolResult:\n    # Your business logic here\n    balance = 1234.56\n    return p.ToolResult(data={\"balance\": balance, \"currency\": \"USD\"})\n\nasync def main() -> None:\n    async with p.Server() as server:\n        agent = await server.create_agent(\n            name=\"Banking Assistant\",\n            description=\"Helpful and professional banking support agent\",\n        )\n\n        # Add behavioral guidelines\n        await agent.create_guideline(\n            condition=\"The customer asks about their balance\",\n            action=\"Retrieve and clearly present their account balance\",\n            tools=[get_account_balance],\n        )\n\n        await agent.create_guideline(\n            condition=\"The customer asks about topics unrelated to banking\",\n            action=\"Politely decline and redirect to banking topics\",\n        )\n\n        # Server runs until shutdown - no additional code needed here.\n        # When the process exits, the context manager handles cleanup automatically.\n\nif __name__ == \"__main__\":\n    asyncio.run(main())\n```\n\nRun with: `python your_agent.py` then open http://localhost:8800\n\n---\n\n## Core Concepts\n\n### 1. Agents\nAI personalities that interact with customers. Created via `server.create_agent()`.\n\nLearn more: [Agents Documentation](https://parlant.io/docs/concepts/agents)\n\n### 2. Guidelines\nNatural language if-then rules that control agent behavior contextually:\n```python\nawait agent.create_guideline(\n    condition=\"When this situation occurs\",  # The trigger\n    action=\"Do this specific thing\",          # The response behavior\n    tools=[optional_tool],                    # Tools available for this guideline\n)\n```\n\nLearn more: [Guidelines Documentation](https://parlant.io/docs/concepts/customization/guidelines)\n\n### 3. Journeys\nStructured multi-step interaction flows (state machines):\n```python\njourney = await agent.create_journey(\n    title=\"Order Support\",\n    description=\"Helps customers with order issues\",\n    conditions=[\"The customer has an order-related question\"],\n)\n\n# Chain states with transitions\nt0 = await journey.initial_state.transition_to(chat_state=\"Ask for order number\")\nt1 = await t0.target.transition_to(tool_state=lookup_order)\nt2 = await t1.target.transition_to(\n    chat_state=\"Present order status\",\n    condition=\"Order was found\",\n)\nawait t2.target.transition_to(state=p.END_JOURNEY)\n```\n\nLearn more: [Journeys Documentation](https://parlant.io/docs/concepts/customization/journeys)\n\n### 4. Tools\nFunctions the agent can call. Always async, always return `ToolResult`:\n```python\n@p.tool\nasync def my_tool(\n    context: p.ToolContext,      # Always first param\n    required_param: str,          # Required parameters\n    optional_param: int = 10,     # Optional with defaults\n) -> p.ToolResult:\n    # Business logic here\n    return p.ToolResult(data={\"key\": \"value\"})\n```\n\nLearn more: [Tools Documentation](https://parlant.io/docs/concepts/customization/tools)\n\n### 5. Glossary Terms\nTeach agents domain-specific terminology:\n```python\nawait agent.create_term(\n    name=\"SKU\",\n    description=\"Stock Keeping Unit - unique product identifier\",\n    synonyms=[\"product code\", \"item number\"],\n)\n```\n\nLearn more: [Glossary Documentation](https://parlant.io/docs/concepts/customization/glossary)\n\n### 6. Canned Responses\nTemplate responses to eliminate hallucination and control language style:\n```python\nawait agent.add_canned_response(\n    key=\"greeting\",\n    content=\"Hello! I'm here to help with your order. How can I assist you today?\",\n)\n```\n\nLearn more: [Canned Responses Documentation](https://parlant.io/docs/concepts/customization/canned-responses)\n\n### 7. Streaming Mode\nAgents can deliver responses in real-time chunks for a more interactive experience:\n```python\nfrom parlant.sdk import MessageOutputMode\n\nagent = await server.create_agent(\n    name=\"Support Agent\",\n    description=\"Helpful support agent\",\n    message_output_mode=MessageOutputMode.STREAMING,  # Enable streaming\n)\n```\n\nOutput modes:\n- `MessageOutputMode.BLOCK` (default): Complete response delivered at once\n- `MessageOutputMode.STREAMING`: Response delivered in real-time chunks with token-by-token animation\n\nStreaming mode provides actual token usage information (input/output tokens) in generation metadata.\n\n---\n\n## Common Patterns\n\n### Pattern: Tool with Customer Context\n```python\n@p.tool\nasync def get_customer_orders(context: p.ToolContext) -> p.ToolResult:\n    # context.customer_id is automatically available\n    orders = await db.get_orders(context.customer_id)\n    return p.ToolResult(data=orders)\n```\n\n### Pattern: Conditional Transitions\n```python\n# Branch based on conditions\nt0 = await journey.initial_state.transition_to(tool_state=check_eligibility)\n\n# Multiple outgoing transitions from same state\nawait t0.target.transition_to(\n    chat_state=\"Approve the request\",\n    condition=\"Customer is eligible\",\n)\nawait t0.target.transition_to(\n    chat_state=\"Explain why they're not eligible\",\n    condition=\"Customer is not eligible\",\n)\n```\n\n### Pattern: Disambiguation\nHandle ambiguous user intents:\n```python\nobservation = await agent.create_observation(\n    \"The customer mentions a problem but doesn't specify what kind\",\n)\nawait observation.disambiguate([billing_journey, technical_support_journey])\n```\n\n### Pattern: Journey-Scoped Guidelines\nGuidelines that only apply within a specific journey:\n```python\nawait journey.create_guideline(\n    condition=\"Customer seems frustrated\",\n    action=\"Acknowledge their frustration and offer to escalate\",\n)\n```\n\n---\n\n## Environment Variables\n\nSet your LLM provider credentials before running. Examples:\n- `OPENAI_API_KEY` - For OpenAI\n- `ANTHROPIC_API_KEY` - For Anthropic\n- `AZURE_OPENAI_API_KEY` + `AZURE_OPENAI_ENDPOINT` - For Azure OpenAI\n\nLearn more: [Installation & Setup](https://parlant.io/docs/quickstart/installation)\n\n---\n\n## Full Example: Customer Service Agent\n\n```python\nimport parlant.sdk as p\nimport asyncio\n\n# Define tools\n@p.tool\nasync def lookup_order(context: p.ToolContext, order_id: str) -> p.ToolResult:\n    # Simulated order lookup\n    return p.ToolResult(data={\n        \"order_id\": order_id,\n        \"status\": \"shipped\",\n        \"tracking\": \"1Z999AA10123456784\",\n    })\n\n@p.tool\nasync def request_refund(context: p.ToolContext, order_id: str, reason: str) -> p.ToolResult:\n    return p.ToolResult(data={\"refund_id\": \"REF-12345\", \"status\": \"processing\"})\n\nasync def create_order_journey(agent: p.Agent) -> p.Journey:\n    journey = await agent.create_journey(\n        title=\"Order Support\",\n        description=\"Helps customers check order status or request refunds\",\n        conditions=[\"Customer asks about an order\"],\n    )\n\n    # Step 1: Get order number\n    t0 = await journey.initial_state.transition_to(\n        chat_state=\"Ask the customer for their order number\"\n    )\n\n    # Step 2: Look up the order\n    t1 = await t0.target.transition_to(tool_state=lookup_order)\n\n    # Step 3a: Order found - show status\n    t2 = await t1.target.transition_to(\n        chat_state=\"Present the order status and tracking information\",\n        condition=\"Order was found\",\n    )\n\n    # Step 3b: Order not found\n    await t1.target.transition_to(\n        chat_state=\"Apologize and ask them to verify the order number\",\n        condition=\"Order was not found\",\n    )\n\n    # Step 4: Check if they need anything else\n    t3 = await t2.target.transition_to(\n        chat_state=\"Ask if they need help with anything else regarding this order\"\n    )\n\n    # Step 5a: They want a refund\n    t4 = await t3.target.transition_to(\n        tool_state=request_refund,\n        condition=\"Customer requests a refund\",\n    )\n    await t4.target.transition_to(\n        chat_state=\"Confirm the refund has been initiated\"\n    )\n\n    # Step 5b: They're satisfied\n    await t3.target.transition_to(\n        state=p.END_JOURNEY,\n        condition=\"Customer has no more questions\",\n    )\n\n    # Journey-specific guidelines\n    await journey.create_guideline(\n        condition=\"Customer is upset about a delayed order\",\n        action=\"Apologize sincerely and offer expedited shipping on their next order\",\n    )\n\n    return journey\n\nasync def main() -> None:\n    async with p.Server() as server:\n        agent = await server.create_agent(\n            name=\"Support Agent\",\n            description=\"Friendly and efficient customer support representative\",\n        )\n\n        # Domain knowledge\n        await agent.create_term(\n            name=\"Express Shipping\",\n            description=\"2-day delivery, costs $9.99\",\n        )\n\n        # Create journey\n        await create_order_journey(agent)\n\n        # Global guidelines (apply everywhere)\n        await agent.create_guideline(\n            condition=\"Customer uses profanity or is abusive\",\n            action=\"Calmly ask them to be respectful, or offer to end the conversation\",\n        )\n\n        await agent.create_guideline(\n            condition=\"Customer asks to speak to a human\",\n            action=\"Provide the support phone number: 1-800-555-0123\",\n        )\n\n        # Server runs until shutdown - no additional code needed here.\n        # When the process exits, the context manager handles cleanup automatically.\n\nif __name__ == \"__main__\":\n    asyncio.run(main())\n```\n\n---\n\n## Testing Framework\n\nParlant includes a testing framework for validating agent behavior using NLP-based assertions.\n\n### Basic Test Structure\n\n```python\nfrom parlant.testing import Suite\nfrom parlant.testing.steps import AgentMessage, CustomerMessage\n\nsuite = Suite(\n    server_url=\"http://localhost:8800\",\n    agent_id=\"your_agent_id\",\n)\n\n@suite.scenario\nasync def test_greeting() -> None:\n    async with suite.session() as session:\n        response = await session.send(\"Hello!\")\n        await response.should(\"be a friendly greeting or offer to help\")\n\n@suite.scenario\nasync def test_appointment_inquiry() -> None:\n    async with suite.session() as session:\n        response = await session.send(\"Can I schedule an appointment?\")\n        await response.should(\"acknowledge the request or ask for more details\")\n```\n\nRun tests with: `parlant-test your_test_file.py`\n\n### NLP-Based Assertions\n\nThe `response.should()` method uses NLP to evaluate conditions against the full conversation context:\n\n```python\n# Single condition\nawait response.should(\"be polite and professional\")\n\n# Multiple conditions (evaluated in parallel)\nawait response.should([\n    \"ask for the reason for the visit\",\n    \"be polite\",\n    \"not mention pricing\",\n])\n```\n\n### Multi-Turn Conversations with unfold()\n\nTest multi-turn conversations where each step builds on history:\n\n```python\n@suite.scenario\nasync def test_booking_flow() -> None:\n    async with suite.session() as session:\n        await session.unfold([\n            # History-only steps (no assertion)\n            CustomerMessage(\"Hello\"),\n            AgentMessage(\"Hi! How can I help you today?\"),\n\n            # Steps with assertions create sub-tests\n            CustomerMessage(\"I need to book an appointment\"),\n            AgentMessage(\n                text=\"What's the reason for your visit?\",\n                should=[\"ask for the reason\", \"be polite\"],\n            ),\n\n            CustomerMessage(\"Regular checkup\"),\n            AgentMessage(\n                text=\"I have Monday at 10am or Wednesday at 2pm available.\",\n                should=\"offer appointment times\",\n            ),\n        ])\n```\n\n**How unfold() works:**\n- `CustomerMessage(text)` - Customer's message in the conversation\n- `AgentMessage(text, should)` - Expected agent response\n  - `text`: Reference response used as history for subsequent tests\n  - `should`: Assertion condition(s). Only steps with `should` create sub-tests\n- Each sub-test gets a fresh session with prefab history of all prior steps\n- Sub-tests run sequentially and report results independently\n\n### Repeated Scenarios\n\nRun the same scenario multiple times for consistency testing:\n\n```python\n@suite.scenario(repetitions=3)\nasync def test_consistent_greeting() -> None:\n    async with suite.session() as session:\n        response = await session.send(\"Hello\")\n        await response.should(\"greet the customer\")\n```\n\n### Hooks\n\n```python\n@suite.before_all\nasync def setup() -> None:\n    # Runs once before all tests\n    suite.context[\"api_key\"] = \"test-key\"\n\n@suite.after_all\nasync def teardown() -> None:\n    # Runs once after all tests\n    pass\n\n@suite.before_each\nasync def before_test(test_name: str) -> None:\n    # Runs before each test\n    pass\n\n@suite.after_each\nasync def after_test(test_name: str, passed: bool, error: str | None) -> None:\n    # Runs after each test\n    pass\n```\n\n### CLI Options\n\n```bash\n# Run all tests\nparlant-test tests.py\n\n# Filter by pattern\nparlant-test tests.py -k \"greeting\"\n\n# Run tests in parallel\nparlant-test tests.py --parallel\n\n# Custom timeout (seconds)\nparlant-test tests.py --timeout 120\n```\n\n---\n\n## Links\n\n- Documentation: https://parlant.io/\n- GitHub: https://github.com/emcie-co/parlant\n- PyPI: https://pypi.org/project/parlant/\n- Discord: https://discord.gg/duxWqxKk6J\n",".cursor/mcp.json":"# Model Context Protocol (MCP) Configuration\n\nPath: `.cursor/mcp.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/emcie-co/parlant/HEAD/.cursor/mcp.json)",".claude-plugin/marketplace.json":"# Claude Plugin Marketplace Catalog\n\nPath: `.claude-plugin/marketplace.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/emcie-co/parlant/HEAD/.claude-plugin/marketplace.json)",".claude-plugin/plugin.json":"# claude-plugin Plugin Manifest\n\nPath: `.claude-plugin/plugin.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/emcie-co/parlant/HEAD/.claude-plugin/plugin.json)"},"files":{"CLAUDE.md":"This is the main repo of Parlant (https://parlant.io).\n\nParlant is a Python based agent framework. Its core strengths:\n\n1. It allows you to create compliant and controlled AI agents for customer-facing use cases\n2. It provides many conversational management features out of the box\n3. It's built for enterprise, large-scale use cases, where SLAs, stability and security are paramount\n\nThe repo's structure follows the Hexagonal Architecture (Ports and Adapters) approach.\n\n- src/parlant\n  - core: Core framework code\n  - adapters: Implementations of interfaces using 3rd party tools\n  - api: REST API layer using FastAPI. Uses modules from core/\n- tests: all tests for the project. Structure strives to mirror that which is under src/parlant.\n\nGeneral Coding Instructions:\n\n- Always ensure you stick to Hexagonal Architecture patterns in line with how they're used in this codebase.\n- Every time you add something, look for similar things in the codebase and ensure you follow the coding style.\n- We use MyPy on strict mode. Every parameter needs to be type-annotated. Every function's result too.\n- If you need to add a test for something, first say where you plan to add it and ask for confirmation.\n- We follow TDD. When you make a change, first create a failing test. Once it fails, implement just enough so it passes.\n- If you need to test classes/methods in sdk.py (or generally to test things that relate to engine behavior) make sure you inherit from SDKTest and understand how it works and how to use it.\n- Test names should go \"test*that*...\" using clear names that explain the context, what is executed, and what is the expected result.\n- You can run tests using pytest. Make sure you run \"uv run pytest tests/path/to/test/file.py\" while also specifying the test name that you need to run.\n\nAlways follow this plan when asked to code a feature or fix a bug:\n\n1. Consider the codebase's structure\n2. Describe your implementation plan, including:\n   a. What tests you will write (test names + files they would live in)\n   b. Why do you think the tests would initially fail\n   c. Where you would plan to implement the code that would make the tests pass\n3. Ask for plan confirmation. If you get feedback, revise your plan and ask for confirmation again until you get it.\n4. Implement the tests first. Ask for confirmation and code review.\n5. Once tests are approved, once again suggest your implementation plan for making them pass, and get plan review until confirmation.\n6. Once your implementation plan is confirmed, go ahead with implementing the code to pass them.\n7. Make sure to format all of the files you changed using ruff (it is installed in the environment).\n8. Run `uv run python scripts/lint.py --mypy --ruff` to ensure your code has no lint issues.\n","llms.txt":"# Parlant\n\n> Open-source AI agent framework for building customer-facing conversational agents with ensured rule compliance and enterprise-grade behavior control.\n\nParlant is a Python framework for building **predictable, business-aligned AI agents**. Unlike prompt-based approaches, Parlant ensures agents follow behavioral rules through structured guideline matching and contextual application.\n\nInstall: `pip install parlant`\n\n## Quick Start Example\n\n```python\nimport parlant.sdk as p\nimport asyncio\n\n@p.tool\nasync def get_account_balance(context: p.ToolContext, account_id: str) -> p.ToolResult:\n    # Your business logic here\n    balance = 1234.56\n    return p.ToolResult(data={\"balance\": balance, \"currency\": \"USD\"})\n\nasync def main() -> None:\n    async with p.Server() as server:\n        agent = await server.create_agent(\n            name=\"Banking Assistant\",\n            description=\"Helpful and professional banking support agent\",\n        )\n\n        # Add behavioral guidelines\n        await agent.create_guideline(\n            condition=\"The customer asks about their balance\",\n            action=\"Retrieve and clearly present their account balance\",\n            tools=[get_account_balance],\n        )\n\n        await agent.create_guideline(\n            condition=\"The customer asks about topics unrelated to banking\",\n            action=\"Politely decline and redirect to banking topics\",\n        )\n\n        # Server runs until shutdown - no additional code needed here.\n        # When the process exits, the context manager handles cleanup automatically.\n\nif __name__ == \"__main__\":\n    asyncio.run(main())\n```\n\nRun with: `python your_agent.py` then open http://localhost:8800\n\n---\n\n## Core Concepts\n\n### 1. Agents\nAI personalities that interact with customers. Created via `server.create_agent()`.\n\nLearn more: [Agents Documentation](https://parlant.io/docs/concepts/agents)\n\n### 2. Guidelines\nNatural language if-then rules that control agent behavior contextually:\n```python\nawait agent.create_guideline(\n    condition=\"When this situation occurs\",  # The trigger\n    action=\"Do this specific thing\",          # The response behavior\n    tools=[optional_tool],                    # Tools available for this guideline\n)\n```\n\nLearn more: [Guidelines Documentation](https://parlant.io/docs/concepts/customization/guidelines)\n\n### 3. Journeys\nStructured multi-step interaction flows (state machines):\n```python\njourney = await agent.create_journey(\n    title=\"Order Support\",\n    description=\"Helps customers with order issues\",\n    conditions=[\"The customer has an order-related question\"],\n)\n\n# Chain states with transitions\nt0 = await journey.initial_state.transition_to(chat_state=\"Ask for order number\")\nt1 = await t0.target.transition_to(tool_state=lookup_order)\nt2 = await t1.target.transition_to(\n    chat_state=\"Present order status\",\n    condition=\"Order was found\",\n)\nawait t2.target.transition_to(state=p.END_JOURNEY)\n```\n\nLearn more: [Journeys Documentation](https://parlant.io/docs/concepts/customization/journeys)\n\n### 4. Tools\nFunctions the agent can call. Always async, always return `ToolResult`:\n```python\n@p.tool\nasync def my_tool(\n    context: p.ToolContext,      # Always first param\n    required_param: str,          # Required parameters\n    optional_param: int = 10,     # Optional with defaults\n) -> p.ToolResult:\n    # Business logic here\n    return p.ToolResult(data={\"key\": \"value\"})\n```\n\nLearn more: [Tools Documentation](https://parlant.io/docs/concepts/customization/tools)\n\n### 5. Glossary Terms\nTeach agents domain-specific terminology:\n```python\nawait agent.create_term(\n    name=\"SKU\",\n    description=\"Stock Keeping Unit - unique product identifier\",\n    synonyms=[\"product code\", \"item number\"],\n)\n```\n\nLearn more: [Glossary Documentation](https://parlant.io/docs/concepts/customization/glossary)\n\n### 6. Canned Responses\nTemplate responses to eliminate hallucination and control language style:\n```python\nawait agent.add_canned_response(\n    key=\"greeting\",\n    content=\"Hello! I'm here to help with your order. How can I assist you today?\",\n)\n```\n\nLearn more: [Canned Responses Documentation](https://parlant.io/docs/concepts/customization/canned-responses)\n\n### 7. Streaming Mode\nAgents can deliver responses in real-time chunks for a more interactive experience:\n```python\nfrom parlant.sdk import MessageOutputMode\n\nagent = await server.create_agent(\n    name=\"Support Agent\",\n    description=\"Helpful support agent\",\n    message_output_mode=MessageOutputMode.STREAMING,  # Enable streaming\n)\n```\n\nOutput modes:\n- `MessageOutputMode.BLOCK` (default): Complete response delivered at once\n- `MessageOutputMode.STREAMING`: Response delivered in real-time chunks with token-by-token animation\n\nStreaming mode provides actual token usage information (input/output tokens) in generation metadata.\n\n---\n\n## Common Patterns\n\n### Pattern: Tool with Customer Context\n```python\n@p.tool\nasync def get_customer_orders(context: p.ToolContext) -> p.ToolResult:\n    # context.customer_id is automatically available\n    orders = await db.get_orders(context.customer_id)\n    return p.ToolResult(data=orders)\n```\n\n### Pattern: Conditional Transitions\n```python\n# Branch based on conditions\nt0 = await journey.initial_state.transition_to(tool_state=check_eligibility)\n\n# Multiple outgoing transitions from same state\nawait t0.target.transition_to(\n    chat_state=\"Approve the request\",\n    condition=\"Customer is eligible\",\n)\nawait t0.target.transition_to(\n    chat_state=\"Explain why they're not eligible\",\n    condition=\"Customer is not eligible\",\n)\n```\n\n### Pattern: Disambiguation\nHandle ambiguous user intents:\n```python\nobservation = await agent.create_observation(\n    \"The customer mentions a problem but doesn't specify what kind\",\n)\nawait observation.disambiguate([billing_journey, technical_support_journey])\n```\n\n### Pattern: Journey-Scoped Guidelines\nGuidelines that only apply within a specific journey:\n```python\nawait journey.create_guideline(\n    condition=\"Customer seems frustrated\",\n    action=\"Acknowledge their frustration and offer to escalate\",\n)\n```\n\n---\n\n## Environment Variables\n\nSet your LLM provider credentials before running. Examples:\n- `OPENAI_API_KEY` - For OpenAI\n- `ANTHROPIC_API_KEY` - For Anthropic\n- `AZURE_OPENAI_API_KEY` + `AZURE_OPENAI_ENDPOINT` - For Azure OpenAI\n\nLearn more: [Installation & Setup](https://parlant.io/docs/quickstart/installation)\n\n---\n\n## Full Example: Customer Service Agent\n\n```python\nimport parlant.sdk as p\nimport asyncio\n\n# Define tools\n@p.tool\nasync def lookup_order(context: p.ToolContext, order_id: str) -> p.ToolResult:\n    # Simulated order lookup\n    return p.ToolResult(data={\n        \"order_id\": order_id,\n        \"status\": \"shipped\",\n        \"tracking\": \"1Z999AA10123456784\",\n    })\n\n@p.tool\nasync def request_refund(context: p.ToolContext, order_id: str, reason: str) -> p.ToolResult:\n    return p.ToolResult(data={\"refund_id\": \"REF-12345\", \"status\": \"processing\"})\n\nasync def create_order_journey(agent: p.Agent) -> p.Journey:\n    journey = await agent.create_journey(\n        title=\"Order Support\",\n        description=\"Helps customers check order status or request refunds\",\n        conditions=[\"Customer asks about an order\"],\n    )\n\n    # Step 1: Get order number\n    t0 = await journey.initial_state.transition_to(\n        chat_state=\"Ask the customer for their order number\"\n    )\n\n    # Step 2: Look up the order\n    t1 = await t0.target.transition_to(tool_state=lookup_order)\n\n    # Step 3a: Order found - show status\n    t2 = await t1.target.transition_to(\n        chat_state=\"Present the order status and tracking information\",\n        condition=\"Order was found\",\n    )\n\n    # Step 3b: Order not found\n    await t1.target.transition_to(\n        chat_state=\"Apologize and ask them to verify the order number\",\n        condition=\"Order was not found\",\n    )\n\n    # Step 4: Check if they need anything else\n    t3 = await t2.target.transition_to(\n        chat_state=\"Ask if they need help with anything else regarding this order\"\n    )\n\n    # Step 5a: They want a refund\n    t4 = await t3.target.transition_to(\n        tool_state=request_refund,\n        condition=\"Customer requests a refund\",\n    )\n    await t4.target.transition_to(\n        chat_state=\"Confirm the refund has been initiated\"\n    )\n\n    # Step 5b: They're satisfied\n    await t3.target.transition_to(\n        state=p.END_JOURNEY,\n        condition=\"Customer has no more questions\",\n    )\n\n    # Journey-specific guidelines\n    await journey.create_guideline(\n        condition=\"Customer is upset about a delayed order\",\n        action=\"Apologize sincerely and offer expedited shipping on their next order\",\n    )\n\n    return journey\n\nasync def main() -> None:\n    async with p.Server() as server:\n        agent = await server.create_agent(\n            name=\"Support Agent\",\n            description=\"Friendly and efficient customer support representative\",\n        )\n\n        # Domain knowledge\n        await agent.create_term(\n            name=\"Express Shipping\",\n            description=\"2-day delivery, costs $9.99\",\n        )\n\n        # Create journey\n        await create_order_journey(agent)\n\n        # Global guidelines (apply everywhere)\n        await agent.create_guideline(\n            condition=\"Customer uses profanity or is abusive\",\n            action=\"Calmly ask them to be respectful, or offer to end the conversation\",\n        )\n\n        await agent.create_guideline(\n            condition=\"Customer asks to speak to a human\",\n            action=\"Provide the support phone number: 1-800-555-0123\",\n        )\n\n        # Server runs until shutdown - no additional code needed here.\n        # When the process exits, the context manager handles cleanup automatically.\n\nif __name__ == \"__main__\":\n    asyncio.run(main())\n```\n\n---\n\n## Testing Framework\n\nParlant includes a testing framework for validating agent behavior using NLP-based assertions.\n\n### Basic Test Structure\n\n```python\nfrom parlant.testing import Suite\nfrom parlant.testing.steps import AgentMessage, CustomerMessage\n\nsuite = Suite(\n    server_url=\"http://localhost:8800\",\n    agent_id=\"your_agent_id\",\n)\n\n@suite.scenario\nasync def test_greeting() -> None:\n    async with suite.session() as session:\n        response = await session.send(\"Hello!\")\n        await response.should(\"be a friendly greeting or offer to help\")\n\n@suite.scenario\nasync def test_appointment_inquiry() -> None:\n    async with suite.session() as session:\n        response = await session.send(\"Can I schedule an appointment?\")\n        await response.should(\"acknowledge the request or ask for more details\")\n```\n\nRun tests with: `parlant-test your_test_file.py`\n\n### NLP-Based Assertions\n\nThe `response.should()` method uses NLP to evaluate conditions against the full conversation context:\n\n```python\n# Single condition\nawait response.should(\"be polite and professional\")\n\n# Multiple conditions (evaluated in parallel)\nawait response.should([\n    \"ask for the reason for the visit\",\n    \"be polite\",\n    \"not mention pricing\",\n])\n```\n\n### Multi-Turn Conversations with unfold()\n\nTest multi-turn conversations where each step builds on history:\n\n```python\n@suite.scenario\nasync def test_booking_flow() -> None:\n    async with suite.session() as session:\n        await session.unfold([\n            # History-only steps (no assertion)\n            CustomerMessage(\"Hello\"),\n            AgentMessage(\"Hi! How can I help you today?\"),\n\n            # Steps with assertions create sub-tests\n            CustomerMessage(\"I need to book an appointment\"),\n            AgentMessage(\n                text=\"What's the reason for your visit?\",\n                should=[\"ask for the reason\", \"be polite\"],\n            ),\n\n            CustomerMessage(\"Regular checkup\"),\n            AgentMessage(\n                text=\"I have Monday at 10am or Wednesday at 2pm available.\",\n                should=\"offer appointment times\",\n            ),\n        ])\n```\n\n**How unfold() works:**\n- `CustomerMessage(text)` - Customer's message in the conversation\n- `AgentMessage(text, should)` - Expected agent response\n  - `text`: Reference response used as history for subsequent tests\n  - `should`: Assertion condition(s). Only steps with `should` create sub-tests\n- Each sub-test gets a fresh session with prefab history of all prior steps\n- Sub-tests run sequentially and report results independently\n\n### Repeated Scenarios\n\nRun the same scenario multiple times for consistency testing:\n\n```python\n@suite.scenario(repetitions=3)\nasync def test_consistent_greeting() -> None:\n    async with suite.session() as session:\n        response = await session.send(\"Hello\")\n        await response.should(\"greet the customer\")\n```\n\n### Hooks\n\n```python\n@suite.before_all\nasync def setup() -> None:\n    # Runs once before all tests\n    suite.context[\"api_key\"] = \"test-key\"\n\n@suite.after_all\nasync def teardown() -> None:\n    # Runs once after all tests\n    pass\n\n@suite.before_each\nasync def before_test(test_name: str) -> None:\n    # Runs before each test\n    pass\n\n@suite.after_each\nasync def after_test(test_name: str, passed: bool, error: str | None) -> None:\n    # Runs after each test\n    pass\n```\n\n### CLI Options\n\n```bash\n# Run all tests\nparlant-test tests.py\n\n# Filter by pattern\nparlant-test tests.py -k \"greeting\"\n\n# Run tests in parallel\nparlant-test tests.py --parallel\n\n# Custom timeout (seconds)\nparlant-test tests.py --timeout 120\n```\n\n---\n\n## Links\n\n- Documentation: https://parlant.io/\n- GitHub: https://github.com/emcie-co/parlant\n- PyPI: https://pypi.org/project/parlant/\n- Discord: https://discord.gg/duxWqxKk6J\n",".cursor/mcp.json":"# Model Context Protocol (MCP) Configuration\n\nPath: `.cursor/mcp.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/emcie-co/parlant/HEAD/.cursor/mcp.json)",".claude-plugin/marketplace.json":"# Claude Plugin Marketplace Catalog\n\nPath: `.claude-plugin/marketplace.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/emcie-co/parlant/HEAD/.claude-plugin/marketplace.json)",".claude-plugin/plugin.json":"# claude-plugin Plugin Manifest\n\nPath: `.claude-plugin/plugin.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/emcie-co/parlant/HEAD/.claude-plugin/plugin.json)"},"items":[{"name":"llms.txt","path":"llms.txt","rawUrl":"https://raw.githubusercontent.com/emcie-co/parlant/HEAD/llms.txt","title":"LLM Index & Context Digest","category":"root-instruction","format":"text","content":"# Parlant\n\n> Open-source AI agent framework for building customer-facing conversational agents with ensured rule compliance and enterprise-grade behavior control.\n\nParlant is a Python framework for building **predictable, business-aligned AI agents**. Unlike prompt-based approaches, Parlant ensures agents follow behavioral rules through structured guideline matching and contextual application.\n\nInstall: `pip install parlant`\n\n## Quick Start Example\n\n```python\nimport parlant.sdk as p\nimport asyncio\n\n@p.tool\nasync def get_account_balance(context: p.ToolContext, account_id: str) -> p.ToolResult:\n    # Your business logic here\n    balance = 1234.56\n    return p.ToolResult(data={\"balance\": balance, \"currency\": \"USD\"})\n\nasync def main() -> None:\n    async with p.Server() as server:\n        agent = await server.create_agent(\n            name=\"Banking Assistant\",\n            description=\"Helpful and professional banking support agent\",\n        )\n\n        # Add behavioral guidelines\n        await agent.create_guideline(\n            condition=\"The customer asks about their balance\",\n            action=\"Retrieve and clearly present their account balance\",\n            tools=[get_account_balance],\n        )\n\n        await agent.create_guideline(\n            condition=\"The customer asks about topics unrelated to banking\",\n            action=\"Politely decline and redirect to banking topics\",\n        )\n\n        # Server runs until shutdown - no additional code needed here.\n        # When the process exits, the context manager handles cleanup automatically.\n\nif __name__ == \"__main__\":\n    asyncio.run(main())\n```\n\nRun with: `python your_agent.py` then open http://localhost:8800\n\n---\n\n## Core Concepts\n\n### 1. Agents\nAI personalities that interact with customers. Created via `server.create_agent()`.\n\nLearn more: [Agents Documentation](https://parlant.io/docs/concepts/agents)\n\n### 2. Guidelines\nNatural language if-then rules that control agent behavior contextually:\n```python\nawait agent.create_guideline(\n    condition=\"When this situation occurs\",  # The trigger\n    action=\"Do this specific thing\",          # The response behavior\n    tools=[optional_tool],                    # Tools available for this guideline\n)\n```\n\nLearn more: [Guidelines Documentation](https://parlant.io/docs/concepts/customization/guidelines)\n\n### 3. Journeys\nStructured multi-step interaction flows (state machines):\n```python\njourney = await agent.create_journey(\n    title=\"Order Support\",\n    description=\"Helps customers with order issues\",\n    conditions=[\"The customer has an order-related question\"],\n)\n\n# Chain states with transitions\nt0 = await journey.initial_state.transition_to(chat_state=\"Ask for order number\")\nt1 = await t0.target.transition_to(tool_state=lookup_order)\nt2 = await t1.target.transition_to(\n    chat_state=\"Present order status\",\n    condition=\"Order was found\",\n)\nawait t2.target.transition_to(state=p.END_JOURNEY)\n```\n\nLearn more: [Journeys Documentation](https://parlant.io/docs/concepts/customization/journeys)\n\n### 4. Tools\nFunctions the agent can call. Always async, always return `ToolResult`:\n```python\n@p.tool\nasync def my_tool(\n    context: p.ToolContext,      # Always first param\n    required_param: str,          # Required parameters\n    optional_param: int = 10,     # Optional with defaults\n) -> p.ToolResult:\n    # Business logic here\n    return p.ToolResult(data={\"key\": \"value\"})\n```\n\nLearn more: [Tools Documentation](https://parlant.io/docs/concepts/customization/tools)\n\n### 5. Glossary Terms\nTeach agents domain-specific terminology:\n```python\nawait agent.create_term(\n    name=\"SKU\",\n    description=\"Stock Keeping Unit - unique product identifier\",\n    synonyms=[\"product code\", \"item number\"],\n)\n```\n\nLearn more: [Glossary Documentation](https://parlant.io/docs/concepts/customization/glossary)\n\n### 6. Canned Responses\nTemplate responses to eliminate hallucination and control language style:\n```python\nawait agent.add_canned_response(\n    key=\"greeting\",\n    content=\"Hello! I'm here to help with your order. How can I assist you today?\",\n)\n```\n\nLearn more: [Canned Responses Documentation](https://parlant.io/docs/concepts/customization/canned-responses)\n\n### 7. Streaming Mode\nAgents can deliver responses in real-time chunks for a more interactive experience:\n```python\nfrom parlant.sdk import MessageOutputMode\n\nagent = await server.create_agent(\n    name=\"Support Agent\",\n    description=\"Helpful support agent\",\n    message_output_mode=MessageOutputMode.STREAMING,  # Enable streaming\n)\n```\n\nOutput modes:\n- `MessageOutputMode.BLOCK` (default): Complete response delivered at once\n- `MessageOutputMode.STREAMING`: Response delivered in real-time chunks with token-by-token animation\n\nStreaming mode provides actual token usage information (input/output tokens) in generation metadata.\n\n---\n\n## Common Patterns\n\n### Pattern: Tool with Customer Context\n```python\n@p.tool\nasync def get_customer_orders(context: p.ToolContext) -> p.ToolResult:\n    # context.customer_id is automatically available\n    orders = await db.get_orders(context.customer_id)\n    return p.ToolResult(data=orders)\n```\n\n### Pattern: Conditional Transitions\n```python\n# Branch based on conditions\nt0 = await journey.initial_state.transition_to(tool_state=check_eligibility)\n\n# Multiple outgoing transitions from same state\nawait t0.target.transition_to(\n    chat_state=\"Approve the request\",\n    condition=\"Customer is eligible\",\n)\nawait t0.target.transition_to(\n    chat_state=\"Explain why they're not eligible\",\n    condition=\"Customer is not eligible\",\n)\n```\n\n### Pattern: Disambiguation\nHandle ambiguous user intents:\n```python\nobservation = await agent.create_observation(\n    \"The customer mentions a problem but doesn't specify what kind\",\n)\nawait observation.disambiguate([billing_journey, technical_support_journey])\n```\n\n### Pattern: Journey-Scoped Guidelines\nGuidelines that only apply within a specific journey:\n```python\nawait journey.create_guideline(\n    condition=\"Customer seems frustrated\",\n    action=\"Acknowledge their frustration and offer to escalate\",\n)\n```\n\n---\n\n## Environment Variables\n\nSet your LLM provider credentials before running. Examples:\n- `OPENAI_API_KEY` - For OpenAI\n- `ANTHROPIC_API_KEY` - For Anthropic\n- `AZURE_OPENAI_API_KEY` + `AZURE_OPENAI_ENDPOINT` - For Azure OpenAI\n\nLearn more: [Installation & Setup](https://parlant.io/docs/quickstart/installation)\n\n---\n\n## Full Example: Customer Service Agent\n\n```python\nimport parlant.sdk as p\nimport asyncio\n\n# Define tools\n@p.tool\nasync def lookup_order(context: p.ToolContext, order_id: str) -> p.ToolResult:\n    # Simulated order lookup\n    return p.ToolResult(data={\n        \"order_id\": order_id,\n        \"status\": \"shipped\",\n        \"tracking\": \"1Z999AA10123456784\",\n    })\n\n@p.tool\nasync def request_refund(context: p.ToolContext, order_id: str, reason: str) -> p.ToolResult:\n    return p.ToolResult(data={\"refund_id\": \"REF-12345\", \"status\": \"processing\"})\n\nasync def create_order_journey(agent: p.Agent) -> p.Journey:\n    journey = await agent.create_journey(\n        title=\"Order Support\",\n        description=\"Helps customers check order status or request refunds\",\n        conditions=[\"Customer asks about an order\"],\n    )\n\n    # Step 1: Get order number\n    t0 = await journey.initial_state.transition_to(\n        chat_state=\"Ask the customer for their order number\"\n    )\n\n    # Step 2: Look up the order\n    t1 = await t0.target.transition_to(tool_state=lookup_order)\n\n    # Step 3a: Order found - show status\n    t2 = await t1.target.transition_to(\n        chat_state=\"Present the order status and tracking information\",\n        condition=\"Order was found\",\n    )\n\n    # Step 3b: Order not found\n    await t1.target.transition_to(\n        chat_state=\"Apologize and ask them to verify the order number\",\n        condition=\"Order was not found\",\n    )\n\n    # Step 4: Check if they need anything else\n    t3 = await t2.target.transition_to(\n        chat_state=\"Ask if they need help with anything else regarding this order\"\n    )\n\n    # Step 5a: They want a refund\n    t4 = await t3.target.transition_to(\n        tool_state=request_refund,\n        condition=\"Customer requests a refund\",\n    )\n    await t4.target.transition_to(\n        chat_state=\"Confirm the refund has been initiated\"\n    )\n\n    # Step 5b: They're satisfied\n    await t3.target.transition_to(\n        state=p.END_JOURNEY,\n        condition=\"Customer has no more questions\",\n    )\n\n    # Journey-specific guidelines\n    await journey.create_guideline(\n        condition=\"Customer is upset about a delayed order\",\n        action=\"Apologize sincerely and offer expedited shipping on their next order\",\n    )\n\n    return journey\n\nasync def main() -> None:\n    async with p.Server() as server:\n        agent = await server.create_agent(\n            name=\"Support Agent\",\n            description=\"Friendly and efficient customer support representative\",\n        )\n\n        # Domain knowledge\n        await agent.create_term(\n            name=\"Express Shipping\",\n            description=\"2-day delivery, costs $9.99\",\n        )\n\n        # Create journey\n        await create_order_journey(agent)\n\n        # Global guidelines (apply everywhere)\n        await agent.create_guideline(\n            condition=\"Customer uses profanity or is abusive\",\n            action=\"Calmly ask them to be respectful, or offer to end the conversation\",\n        )\n\n        await agent.create_guideline(\n            condition=\"Customer asks to speak to a human\",\n            action=\"Provide the support phone number: 1-800-555-0123\",\n        )\n\n        # Server runs until shutdown - no additional code needed here.\n        # When the process exits, the context manager handles cleanup automatically.\n\nif __name__ == \"__main__\":\n    asyncio.run(main())\n```\n\n---\n\n## Testing Framework\n\nParlant includes a testing framework for validating agent behavior using NLP-based assertions.\n\n### Basic Test Structure\n\n```python\nfrom parlant.testing import Suite\nfrom parlant.testing.steps import AgentMessage, CustomerMessage\n\nsuite = Suite(\n    server_url=\"http://localhost:8800\",\n    agent_id=\"your_agent_id\",\n)\n\n@suite.scenario\nasync def test_greeting() -> None:\n    async with suite.session() as session:\n        response = await session.send(\"Hello!\")\n        await response.should(\"be a friendly greeting or offer to help\")\n\n@suite.scenario\nasync def test_appointment_inquiry() -> None:\n    async with suite.session() as session:\n        response = await session.send(\"Can I schedule an appointment?\")\n        await response.should(\"acknowledge the request or ask for more details\")\n```\n\nRun tests with: `parlant-test your_test_file.py`\n\n### NLP-Based Assertions\n\nThe `response.should()` method uses NLP to evaluate conditions against the full conversation context:\n\n```python\n# Single condition\nawait response.should(\"be polite and professional\")\n\n# Multiple conditions (evaluated in parallel)\nawait response.should([\n    \"ask for the reason for the visit\",\n    \"be polite\",\n    \"not mention pricing\",\n])\n```\n\n### Multi-Turn Conversations with unfold()\n\nTest multi-turn conversations where each step builds on history:\n\n```python\n@suite.scenario\nasync def test_booking_flow() -> None:\n    async with suite.session() as session:\n        await session.unfold([\n            # History-only steps (no assertion)\n            CustomerMessage(\"Hello\"),\n            AgentMessage(\"Hi! How can I help you today?\"),\n\n            # Steps with assertions create sub-tests\n            CustomerMessage(\"I need to book an appointment\"),\n            AgentMessage(\n                text=\"What's the reason for your visit?\",\n                should=[\"ask for the reason\", \"be polite\"],\n            ),\n\n            CustomerMessage(\"Regular checkup\"),\n            AgentMessage(\n                text=\"I have Monday at 10am or Wednesday at 2pm available.\",\n                should=\"offer appointment times\",\n            ),\n        ])\n```\n\n**How unfold() works:**\n- `CustomerMessage(text)` - Customer's message in the conversation\n- `AgentMessage(text, should)` - Expected agent response\n  - `text`: Reference response used as history for subsequent tests\n  - `should`: Assertion condition(s). Only steps with `should` create sub-tests\n- Each sub-test gets a fresh session with prefab history of all prior steps\n- Sub-tests run sequentially and report results independently\n\n### Repeated Scenarios\n\nRun the same scenario multiple times for consistency testing:\n\n```python\n@suite.scenario(repetitions=3)\nasync def test_consistent_greeting() -> None:\n    async with suite.session() as session:\n        response = await session.send(\"Hello\")\n        await response.should(\"greet the customer\")\n```\n\n### Hooks\n\n```python\n@suite.before_all\nasync def setup() -> None:\n    # Runs once before all tests\n    suite.context[\"api_key\"] = \"test-key\"\n\n@suite.after_all\nasync def teardown() -> None:\n    # Runs once after all tests\n    pass\n\n@suite.before_each\nasync def before_test(test_name: str) -> None:\n    # Runs before each test\n    pass\n\n@suite.after_each\nasync def after_test(test_name: str, passed: bool, error: str | None) -> None:\n    # Runs after each test\n    pass\n```\n\n### CLI Options\n\n```bash\n# Run all tests\nparlant-test tests.py\n\n# Filter by pattern\nparlant-test tests.py -k \"greeting\"\n\n# Run tests in parallel\nparlant-test tests.py --parallel\n\n# Custom timeout (seconds)\nparlant-test tests.py --timeout 120\n```\n\n---\n\n## Links\n\n- Documentation: https://parlant.io/\n- GitHub: https://github.com/emcie-co/parlant\n- PyPI: https://pypi.org/project/parlant/\n- Discord: https://discord.gg/duxWqxKk6J\n","isInternal":false,"tokens":3011,"sizeBytes":13632},{"name":"CLAUDE.md","path":"CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/emcie-co/parlant/HEAD/CLAUDE.md","title":"Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"This is the main repo of Parlant (https://parlant.io).\n\nParlant is a Python based agent framework. Its core strengths:\n\n1. It allows you to create compliant and controlled AI agents for customer-facing use cases\n2. It provides many conversational management features out of the box\n3. It's built for enterprise, large-scale use cases, where SLAs, stability and security are paramount\n\nThe repo's structure follows the Hexagonal Architecture (Ports and Adapters) approach.\n\n- src/parlant\n  - core: Core framework code\n  - adapters: Implementations of interfaces using 3rd party tools\n  - api: REST API layer using FastAPI. Uses modules from core/\n- tests: all tests for the project. Structure strives to mirror that which is under src/parlant.\n\nGeneral Coding Instructions:\n\n- Always ensure you stick to Hexagonal Architecture patterns in line with how they're used in this codebase.\n- Every time you add something, look for similar things in the codebase and ensure you follow the coding style.\n- We use MyPy on strict mode. Every parameter needs to be type-annotated. Every function's result too.\n- If you need to add a test for something, first say where you plan to add it and ask for confirmation.\n- We follow TDD. When you make a change, first create a failing test. Once it fails, implement just enough so it passes.\n- If you need to test classes/methods in sdk.py (or generally to test things that relate to engine behavior) make sure you inherit from SDKTest and understand how it works and how to use it.\n- Test names should go \"test*that*...\" using clear names that explain the context, what is executed, and what is the expected result.\n- You can run tests using pytest. Make sure you run \"uv run pytest tests/path/to/test/file.py\" while also specifying the test name that you need to run.\n\nAlways follow this plan when asked to code a feature or fix a bug:\n\n1. Consider the codebase's structure\n2. Describe your implementation plan, including:\n   a. What tests you will write (test names + files they would live in)\n   b. Why do you think the tests would initially fail\n   c. Where you would plan to implement the code that would make the tests pass\n3. Ask for plan confirmation. If you get feedback, revise your plan and ask for confirmation again until you get it.\n4. Implement the tests first. Ask for confirmation and code review.\n5. Once tests are approved, once again suggest your implementation plan for making them pass, and get plan review until confirmation.\n6. Once your implementation plan is confirmed, go ahead with implementing the code to pass them.\n7. Make sure to format all of the files you changed using ruff (it is installed in the environment).\n8. Run `uv run python scripts/lint.py --mypy --ruff` to ensure your code has no lint issues.\n","isInternal":false,"tokens":620,"sizeBytes":2772},{"name":"marketplace.json","path":".claude-plugin/marketplace.json","rawUrl":"https://raw.githubusercontent.com/emcie-co/parlant/HEAD/.claude-plugin/marketplace.json","title":"Claude Plugin Marketplace Catalog","category":"marketplace","format":"json","content":"# Claude Plugin Marketplace Catalog\n\nPath: `.claude-plugin/marketplace.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/emcie-co/parlant/HEAD/.claude-plugin/marketplace.json)","tokens":300,"sizeBytes":195},{"name":"plugin.json","path":".claude-plugin/plugin.json","rawUrl":"https://raw.githubusercontent.com/emcie-co/parlant/HEAD/.claude-plugin/plugin.json","title":"claude-plugin Plugin Manifest","category":"plugin-manifest","format":"json","content":"# claude-plugin Plugin Manifest\n\nPath: `.claude-plugin/plugin.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/emcie-co/parlant/HEAD/.claude-plugin/plugin.json)","tokens":300,"sizeBytes":181},{"name":"mcp.json","path":".cursor/mcp.json","rawUrl":"https://raw.githubusercontent.com/emcie-co/parlant/HEAD/.cursor/mcp.json","title":"Model Context Protocol (MCP) Configuration","category":"mcp-config","format":"json","content":"# Model Context Protocol (MCP) Configuration\n\nPath: `.cursor/mcp.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/emcie-co/parlant/HEAD/.cursor/mcp.json)","tokens":300,"sizeBytes":174}],"systemPromptSnippet":"<yakaai_skills repo=\"emcie-co/parlant\">\n<!-- File: llms.txt (Tokens: ~3011 | Category: root-instruction) -->\n# Parlant\n\n> Open-source AI agent framework for building customer-facing conversational agents with ensured rule compliance and enterprise-grade behavior control.\n\nParlant is a Python framework for building **predictable, business-aligned AI agents**. Unlike prompt-based approaches, Parlant ensures agents follow behavioral rules through structured guideline matching and contextual application.\n\nInstall: `pip install parlant`\n\n## Quick Start Example\n\n```python\nimport parlant.sdk as p\nimport asyncio\n\n@p.tool\nasync def get_account_balance(context: p.ToolContext, account_id: str) -> p.ToolResult:\n    # Your business logic here\n    balance = 1234.56\n    return p.ToolResult(data={\"balance\": balance, \"currency\": \"USD\"})\n\nasync def main() -> None:\n    async with p.Server() as server:\n        agent = await server.create_agent(\n            name=\"Banking Assistant\",\n            description=\"Helpful and professional banking support agent\",\n        )\n\n        # Add behavioral guidelines\n        await agent.create_guideline(\n            condition=\"The customer asks about their balance\",\n            action=\"Retrieve and clearly present their account balance\",\n            tools=[get_account_balance],\n        )\n\n        await agent.create_guideline(\n            condition=\"The customer asks about topics unrelated to banking\",\n            action=\"Politely decline and redirect to banking topics\",\n        )\n\n        # Server runs until shutdown - no additional code needed here.\n        # When the process exits, the context manager handles cleanup automatically.\n\nif __name__ == \"__main__\":\n    asyncio.run(main())\n```\n\nRun with: `python your_agent.py` then open http://localhost:8800\n\n---\n\n## Core Concepts\n\n### 1. Agents\nAI personalities that interact with customers. Created via `server.create_agent()`.\n\nLearn more: [Agents Documentation](https://parlant.io/docs/concepts/agents)\n\n### 2. Guidelines\nNatural language if-then rules that control agent behavior contextually:\n```python\nawait agent.create_guideline(\n    condition=\"When this situation occurs\",  # The trigger\n    action=\"Do this specific thing\",          # The response behavior\n    tools=[optional_tool],                    # Tools available for this guideline\n)\n```\n\nLearn more: [Guidelines Documentation](https://parlant.io/docs/concepts/customization/guidelines)\n\n### 3. Journeys\nStructured multi-step interaction flows (state machines):\n```python\njourney = await agent.create_journey(\n    title=\"Order Support\",\n    description=\"Helps customers with order issues\",\n    conditions=[\"The customer has an order-related question\"],\n)\n\n# Chain states with transitions\nt0 = await journey.initial_state.transition_to(chat_state=\"Ask for order number\")\nt1 = await t0.target.transition_to(tool_state=lookup_order)\nt2 = await t1.target.transition_to(\n    chat_state=\"Present order status\",\n    condition=\"Order was found\",\n)\nawait t2.target.transition_to(state=p.END_JOURNEY)\n```\n\nLearn more: [Journeys Documentation](https://parlant.io/docs/concepts/customization/journeys)\n\n### 4. Tools\nFunctions the agent can call. Always async, always return `ToolResult`:\n```python\n@p.tool\nasync def my_tool(\n    context: p.ToolContext,      # Always first param\n    required_param: str,          # Required parameters\n    optional_param: int = 10,     # Optional with defaults\n) -> p.ToolResult:\n    # Business logic here\n    return p.ToolResult(data={\"key\": \"value\"})\n```\n\nLearn more: [Tools Documentation](https://parlant.io/docs/concepts/customization/tools)\n\n### 5. Glossary Terms\nTeach agents domain-specific terminology:\n```python\nawait agent.create_term(\n    name=\"SKU\",\n    description=\"Stock Keeping Unit - unique product identifier\",\n    synonyms=[\"product code\", \"item number\"],\n)\n```\n\nLearn more: [Glossary Documentation](https://parlant.io/docs/concepts/customization/glossary)\n\n### 6. Canned Responses\nTemplate responses to eliminate hallucination and control language style:\n```python\nawait agent.add_canned_response(\n    key=\"greeting\",\n    content=\"Hello! I'm here to help with your order. How can I assist you today?\",\n)\n```\n\nLearn more: [Canned Responses Documentation](https://parlant.io/docs/concepts/customization/canned-responses)\n\n### 7. Streaming Mode\nAgents can deliver responses in real-time chunks for a more interactive experience:\n```python\nfrom parlant.sdk import MessageOutputMode\n\nagent = await server.create_agent(\n    name=\"Support Agent\",\n    description=\"Helpful support agent\",\n    message_output_mode=MessageOutputMode.STREAMING,  # Enable streaming\n)\n```\n\nOutput modes:\n- `MessageOutputMode.BLOCK` (default): Complete response delivered at once\n- `MessageOutputMode.STREAMING`: Response delivered in real-time chunks with token-by-token animation\n\nStreaming mode provides actual token usage information (input/output tokens) in generation metadata.\n\n---\n\n## Common Patterns\n\n### Pattern: Tool with Customer Context\n```python\n@p.tool\nasync def get_customer_orders(context: p.ToolContext) -> p.ToolResult:\n    # context.customer_id is automatically available\n    orders = await db.get_orders(context.customer_id)\n    return p.ToolResult(data=orders)\n```\n\n### Pattern: Conditional Transitions\n```python\n# Branch based on conditions\nt0 = await journey.initial_state.transition_to(tool_state=check_eligibility)\n\n# Multiple outgoing transitions from same state\nawait t0.target.transition_to(\n    chat_state=\"Approve the request\",\n    condition=\"Customer is eligible\",\n)\nawait t0.target.transition_to(\n    chat_state=\"Explain why they're not eligible\",\n    condition=\"Customer is not eligible\",\n)\n```\n\n### Pattern: Disambiguation\nHandle ambiguous user intents:\n```python\nobservation = await agent.create_observation(\n    \"The customer mentions a problem but doesn't specify what kind\",\n)\nawait observation.disambiguate([billing_journey, technical_support_journey])\n```\n\n### Pattern: Journey-Scoped Guidelines\nGuidelines that only apply within a specific journey:\n```python\nawait journey.create_guideline(\n    condition=\"Customer seems frustrated\",\n    action=\"Acknowledge their frustration and offer to escalate\",\n)\n```\n\n---\n\n## Environment Variables\n\nSet your LLM provider credentials before running. Examples:\n- `OPENAI_API_KEY` - For OpenAI\n- `ANTHROPIC_API_KEY` - For Anthropic\n- `AZURE_OPENAI_API_KEY` + `AZURE_OPENAI_ENDPOINT` - For Azure OpenAI\n\nLearn more: [Installation & Setup](https://parlant.io/docs/quickstart/installation)\n\n---\n\n## Full Example: Customer Service Agent\n\n```python\nimport parlant.sdk as p\nimport asyncio\n\n# Define tools\n@p.tool\nasync def lookup_order(context: p.ToolContext, order_id: str) -> p.ToolResult:\n    # Simulated order lookup\n    return p.ToolResult(data={\n        \"order_id\": order_id,\n        \"status\": \"shipped\",\n        \"tracking\": \"1Z999AA10123456784\",\n    })\n\n@p.tool\nasync def request_refund(context: p.ToolContext, order_id: str, reason: str) -> p.ToolResult:\n    return p.ToolResult(data={\"refund_id\": \"REF-12345\", \"status\": \"processing\"})\n\nasync def create_order_journey(agent: p.Agent) -> p.Journey:\n    journey = await agent.create_journey(\n        title=\"Order Support\",\n        description=\"Helps customers check order status or request refunds\",\n        conditions=[\"Customer asks about an order\"],\n    )\n\n    # Step 1: Get order number\n    t0 = await journey.initial_state.transition_to(\n        chat_state=\"Ask the customer for their order number\"\n    )\n\n    # Step 2: Look up the order\n    t1 = await t0.target.transition_to(tool_state=lookup_order)\n\n    # Step 3a: Order found - show status\n    t2 = await t1.target.transition_to(\n        chat_state=\"Present the order status and tracking information\",\n        condition=\"Order was found\",\n    )\n\n    # Step 3b: Order not found\n    await t1.target.transition_to(\n        chat_state=\"Apologize and ask them to verify the order number\",\n        condition=\"Order was not found\",\n    )\n\n    # Step 4: Check if they need anything else\n    t3 = await t2.target.transition_to(\n        chat_state=\"Ask if they need help with anything else regarding this order\"\n    )\n\n    # Step 5a: They want a refund\n    t4 = await t3.target.transition_to(\n        tool_state=request_refund,\n        condition=\"Customer requests a refund\",\n    )\n    await t4.target.transition_to(\n        chat_state=\"Confirm the refund has been initiated\"\n    )\n\n    # Step 5b: They're satisfied\n    await t3.target.transition_to(\n        state=p.END_JOURNEY,\n        condition=\"Customer has no more questions\",\n    )\n\n    # Journey-specific guidelines\n    await journey.create_guideline(\n        condition=\"Customer is upset about a delayed order\",\n        action=\"Apologize sincerely and offer expedited shipping on their next order\",\n    )\n\n    return journey\n\nasync def main() -> None:\n    async with p.Server() as server:\n        agent = await server.create_agent(\n            name=\"Support Agent\",\n            description=\"Friendly and efficient customer support representative\",\n        )\n\n        # Domain knowledge\n        await agent.create_term(\n            name=\"Express Shipping\",\n            description=\"2-day delivery, costs $9.99\",\n        )\n\n        # Create journey\n        await create_order_journey(agent)\n\n        # Global guidelines (apply everywhere)\n        await agent.create_guideline(\n            condition=\"Customer uses profanity or is abusive\",\n            action=\"Calmly ask them to be respectful, or offer to end the conversation\",\n        )\n\n        await agent.create_guideline(\n            condition=\"Customer asks to speak to a human\",\n            action=\"Provide the support phone number: 1-800-555-0123\",\n        )\n\n        # Server runs until shutdown - no additional code needed here.\n        # When the process exits, the context manager handles cleanup automatically.\n\nif __name__ == \"__main__\":\n    asyncio.run(main())\n```\n\n---\n\n## Testing Framework\n\nParlant includes a testing framework for validating agent behavior using NLP-based assertions.\n\n### Basic Test Structure\n\n```python\nfrom parlant.testing import Suite\nfrom parlant.testing.steps import AgentMessage, CustomerMessage\n\nsuite = Suite(\n    server_url=\"http://localhost:8800\",\n    agent_id=\"your_agent_id\",\n)\n\n@suite.scenario\nasync def test_greeting() -> None:\n    async with suite.session() as session:\n        response = await session.send(\"Hello!\")\n        await response.should(\"be a friendly greeting or offer to help\")\n\n@suite.scenario\nasync def test_appointment_inquiry() -> None:\n    async with suite.session() as session:\n        response = await session.send(\"Can I schedule an appointment?\")\n        await response.should(\"acknowledge the request or ask for more details\")\n```\n\nRun tests with: `parlant-test your_test_file.py`\n\n### NLP-Based Assertions\n\nThe `response.should()` method uses NLP to evaluate conditions against the full conversation context:\n\n```python\n# Single condition\nawait response.should(\"be polite and professional\")\n\n# Multiple conditions (evaluated in parallel)\nawait response.should([\n    \"ask for the reason for the visit\",\n    \"be polite\",\n    \"not mention pricing\",\n])\n```\n\n### Multi-Turn Conversations with unfold()\n\nTest multi-turn conversations where each step builds on history:\n\n```python\n@suite.scenario\nasync def test_booking_flow() -> None:\n    async with suite.session() as session:\n        await session.unfold([\n            # History-only steps (no assertion)\n            CustomerMessage(\"Hello\"),\n            AgentMessage(\"Hi! How can I help you today?\"),\n\n            # Steps with assertions create sub-tests\n            CustomerMessage(\"I need to book an appointment\"),\n            AgentMessage(\n                text=\"What's the reason for your visit?\",\n                should=[\"ask for the reason\", \"be polite\"],\n            ),\n\n            CustomerMessage(\"Regular checkup\"),\n            AgentMessage(\n                text=\"I have Monday at 10am or Wednesday at 2pm available.\",\n                should=\"offer appointment times\",\n            ),\n        ])\n```\n\n**How unfold() works:**\n- `CustomerMessage(text)` - Customer's message in the conversation\n- `AgentMessage(text, should)` - Expected agent response\n  - `text`: Reference response used as history for subsequent tests\n  - `should`: Assertion condition(s). Only steps with `should` create sub-tests\n- Each sub-test gets a fresh session with prefab history of all prior steps\n- Sub-tests run sequentially and report results independently\n\n### Repeated Scenarios\n\nRun the same scenario multiple times for consistency testing:\n\n```python\n@suite.scenario(repetitions=3)\nasync def test_consistent_greeting() -> None:\n    async with suite.session() as session:\n        response = await session.send(\"Hello\")\n        await response.should(\"greet the customer\")\n```\n\n### Hooks\n\n```python\n@suite.before_all\nasync def setup() -> None:\n    # Runs once before all tests\n    suite.context[\"api_key\"] = \"test-key\"\n\n@suite.after_all\nasync def teardown() -> None:\n    # Runs once after all tests\n    pass\n\n@suite.before_each\nasync def before_test(test_name: str) -> None:\n    # Runs before each test\n    pass\n\n@suite.after_each\nasync def after_test(test_name: str, passed: bool, error: str | None) -> None:\n    # Runs after each test\n    pass\n```\n\n### CLI Options\n\n```bash\n# Run all tests\nparlant-test tests.py\n\n# Filter by pattern\nparlant-test tests.py -k \"greeting\"\n\n# Run tests in parallel\nparlant-test tests.py --parallel\n\n# Custom timeout (seconds)\nparlant-test tests.py --timeout 120\n```\n\n---\n\n## Links\n\n- Documentation: https://parlant.io/\n- GitHub: https://github.com/emcie-co/parlant\n- PyPI: https://pypi.org/project/parlant/\n- Discord: https://discord.gg/duxWqxKk6J\n\n<!-- File: CLAUDE.md (Tokens: ~620 | Category: claude-rule) -->\nThis is the main repo of Parlant (https://parlant.io).\n\nParlant is a Python based agent framework. Its core strengths:\n\n1. It allows you to create compliant and controlled AI agents for customer-facing use cases\n2. It provides many conversational management features out of the box\n3. It's built for enterprise, large-scale use cases, where SLAs, stability and security are paramount\n\nThe repo's structure follows the Hexagonal Architecture (Ports and Adapters) approach.\n\n- src/parlant\n  - core: Core framework code\n  - adapters: Implementations of interfaces using 3rd party tools\n  - api: REST API layer using FastAPI. Uses modules from core/\n- tests: all tests for the project. Structure strives to mirror that which is under src/parlant.\n\nGeneral Coding Instructions:\n\n- Always ensure you stick to Hexagonal Architecture patterns in line with how they're used in this codebase.\n- Every time you add something, look for similar things in the codebase and ensure you follow the coding style.\n- We use MyPy on strict mode. Every parameter needs to be type-annotated. Every function's result too.\n- If you need to add a test for something, first say where you plan to add it and ask for confirmation.\n- We follow TDD. When you make a change, first create a failing test. Once it fails, implement just enough so it passes.\n- If you need to test classes/methods in sdk.py (or generally to test things that relate to engine behavior) make sure you inherit from SDKTest and understand how it works and how to use it.\n- Test names should go \"test*that*...\" using clear names that explain the context, what is executed, and what is the expected result.\n- You can run tests using pytest. Make sure you run \"uv run pytest tests/path/to/test/file.py\" while also specifying the test name that you need to run.\n\nAlways follow this plan when asked to code a feature or fix a bug:\n\n1. Consider the codebase's structure\n2. Describe your implementation plan, including:\n   a. What tests you will write (test names + files they would live in)\n   b. Why do you think the tests would initially fail\n   c. Where you would plan to implement the code that would make the tests pass\n3. Ask for plan confirmation. If you get feedback, revise your plan and ask for confirmation again until you get it.\n4. Implement the tests first. Ask for confirmation and code review.\n5. Once tests are approved, once again suggest your implementation plan for making them pass, and get plan review until confirmation.\n6. Once your implementation plan is confirmed, go ahead with implementing the code to pass them.\n7. Make sure to format all of the files you changed using ruff (it is installed in the environment).\n8. Run `uv run python scripts/lint.py --mypy --ruff` to ensure your code has no lint issues.\n\n<!-- File: .claude-plugin/marketplace.json (Tokens: ~300 | Category: marketplace) -->\n# Claude Plugin Marketplace Catalog\n\nPath: `.claude-plugin/marketplace.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/emcie-co/parlant/HEAD/.claude-plugin/marketplace.json)\n\n<!-- File: .claude-plugin/plugin.json (Tokens: ~300 | Category: plugin-manifest) -->\n# claude-plugin Plugin Manifest\n\nPath: `.claude-plugin/plugin.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/emcie-co/parlant/HEAD/.claude-plugin/plugin.json)\n\n<!-- File: .cursor/mcp.json (Tokens: ~300 | Category: mcp-config) -->\n# Model Context Protocol (MCP) Configuration\n\nPath: `.cursor/mcp.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/emcie-co/parlant/HEAD/.cursor/mcp.json)\n</yakaai_skills>"}