├── .devcontainer ├── devcontainer.json └── post-create.sh ├── .gitattributes ├── .github └── workflows │ ├── build.yaml │ ├── docs.yml │ ├── e2e-tests.yaml │ └── release.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── cookbook ├── agents │ ├── basic_oai_agent_react.ipynb │ ├── openapi │ │ ├── README.md │ │ └── react_agent_openapi_msgraph.ipynb │ └── weather │ │ ├── README.md │ │ ├── react_agent.ipynb │ │ ├── toolcall_agent.ipynb │ │ └── tools.py ├── arxiv_search.ipynb ├── executors │ └── local.ipynb ├── graphstores │ └── neo4j_basic_examples.ipynb ├── llm │ ├── azure_openai_chat_basic.ipynb │ ├── basic-azopenai-chat.prompty │ ├── basic-hf-chat.prompty │ ├── basic-nvidia-chat.prompty │ ├── basic-openai-chat-history.prompty │ ├── basic-openai-chat.prompty │ ├── elevenlabs_speech_basic.ipynb │ ├── hf_chat_basic.ipynb │ ├── nvidia_chat_basic.ipynb │ ├── nvidia_chat_structured_output.ipynb │ ├── nvidia_embeddings_basic.ipynb │ ├── openai_audio_basic.ipynb │ ├── openai_chat_basic.ipynb │ ├── openai_chat_structured_output.ipynb │ └── openai_embeddings_basic.ipynb ├── mcp │ ├── basic │ │ ├── README.md │ │ ├── requirements.txt │ │ ├── server.py │ │ ├── sse.ipynb │ │ ├── stdio.ipynb │ │ └── tools.py │ └── workflow │ │ ├── README.md │ │ ├── app.py │ │ ├── client.py │ │ ├── components │ │ ├── pubsub.yaml │ │ ├── statestore.yaml │ │ └── workflowstate.yaml │ │ ├── requirements.txt │ │ ├── server.py │ │ └── tools.py ├── vectorstores │ ├── chroma_openai_embeddings.ipynb │ ├── chroma_sentencetransformers_all-MiniLM-L6-v2.ipynb │ └── postgres_sentencetransformers_all-MiniLM-L6-v2.ipynb └── workflows │ ├── basic │ ├── components │ │ └── workflowstate.yaml │ ├── wf_taskchain_dapr_activity.py │ ├── wf_taskchain_dapr_agents_activity.py │ ├── wf_taskchain_dapr_agents_activity_async.py │ ├── wf_taskchain_dapr_agents_oai_llm_request.py │ ├── wf_taskchain_dapr_agents_oai_llm_request_async.py │ ├── wf_taskchain_dapr_agents_oai_structured_output.py │ ├── wf_taskchain_dapr_agents_oai_structured_output_async.py │ └── wf_taskchain_dapr_oai_llm_request.py │ ├── doc2podcast │ ├── README.md │ ├── components │ │ └── workflowstate.yaml │ ├── config.json │ ├── podcast_dialogue.json │ ├── requirements.txt │ └── workflow.py │ ├── llm │ └── multi_modal_task_chain.py │ ├── multi_agents │ ├── basic_calc_agent_as_actor │ │ ├── LLMOrchestrator_state.json │ │ ├── README.md │ │ ├── calculator_agent.py │ │ ├── client.py │ │ ├── components │ │ │ ├── agentstatestore.yaml │ │ │ ├── pubsub.yaml │ │ │ └── workflowstatestore.yaml │ │ ├── llm_orchestrator.py │ │ └── requirements.txt │ ├── basic_lotr_agents_as_actors │ │ ├── README.md │ │ ├── components │ │ │ ├── agentstate.yaml │ │ │ ├── pubsub.yaml │ │ │ └── workflowstate.yaml │ │ ├── dapr.yaml │ │ └── services │ │ │ ├── elf │ │ │ └── app.py │ │ │ ├── hobbit │ │ │ └── app.py │ │ │ ├── wizard │ │ │ └── app.py │ │ │ ├── workflow-llm │ │ │ └── app.py │ │ │ ├── workflow-random │ │ │ └── app.py │ │ │ └── workflow-roundrobin │ │ │ └── app.py │ └── basic_lotr_agents_as_workflows │ │ ├── components │ │ ├── agentsregistry.yaml │ │ ├── pubsub.yaml │ │ └── workflowstate.yaml │ │ ├── dapr.yaml │ │ └── services │ │ ├── dwarf │ │ └── app.py │ │ ├── eagle │ │ └── app.py │ │ ├── elf │ │ └── app.py │ │ ├── hobbit │ │ └── app.py │ │ ├── orchestrator │ │ └── app.py │ │ ├── ranger │ │ └── app.py │ │ ├── wizard │ │ └── app.py │ │ ├── workflow-random │ │ └── app.py │ │ └── workflow-roundrobin │ │ └── app.py │ └── single_agent │ ├── app.py │ ├── client.py │ └── components │ ├── pubsub.yaml │ ├── statestore.yaml │ └── workflowstate.yaml ├── dapr_agents ├── __init__.py ├── agent │ ├── __init__.py │ ├── actor │ │ ├── __init__.py │ │ ├── agent.py │ │ ├── base.py │ │ ├── interface.py │ │ ├── schemas.py │ │ └── service.py │ ├── base.py │ ├── patterns │ │ ├── __init__.py │ │ ├── openapi │ │ │ ├── __init__.py │ │ │ ├── react.py │ │ │ └── tools.py │ │ ├── react │ │ │ ├── __init__.py │ │ │ └── base.py │ │ └── toolcall │ │ │ ├── __init__.py │ │ │ └── base.py │ ├── telemetry │ │ ├── __init__.py │ │ └── otel.py │ └── utils │ │ ├── __init__.py │ │ ├── auth.py │ │ ├── factory.py │ │ ├── message_converter.py │ │ └── text_printer.py ├── document │ ├── __init__.py │ ├── embedder │ │ ├── __init__.py │ │ ├── base.py │ │ ├── nvidia.py │ │ ├── openai.py │ │ └── sentence.py │ ├── fetcher │ │ ├── __init__.py │ │ ├── arxiv.py │ │ └── base.py │ ├── reader │ │ ├── __init__.py │ │ ├── base.py │ │ ├── pdf │ │ │ ├── __init__.py │ │ │ ├── pymupdf.py │ │ │ └── pypdf.py │ │ └── text.py │ └── splitter │ │ ├── __init__.py │ │ ├── base.py │ │ └── text.py ├── executors │ ├── __init__.py │ ├── base.py │ ├── docker.py │ ├── local.py │ ├── sandbox.py │ └── utils │ │ └── package_manager.py ├── llm │ ├── __init__.py │ ├── base.py │ ├── chat.py │ ├── dapr │ │ ├── __init__.py │ │ ├── chat.py │ │ └── client.py │ ├── elevenlabs │ │ ├── __init__.py │ │ ├── client.py │ │ └── speech.py │ ├── huggingface │ │ ├── __init__.py │ │ ├── chat.py │ │ └── client.py │ ├── nvidia │ │ ├── __init__.py │ │ ├── chat.py │ │ ├── client.py │ │ └── embeddings.py │ ├── openai │ │ ├── __init__.py │ │ ├── audio.py │ │ ├── chat.py │ │ ├── client │ │ │ ├── __init__.py │ │ │ ├── azure.py │ │ │ ├── base.py │ │ │ └── openai.py │ │ └── embeddings.py │ └── utils │ │ ├── __init__.py │ │ ├── http.py │ │ ├── request.py │ │ ├── response.py │ │ ├── stream.py │ │ └── structure.py ├── memory │ ├── __init__.py │ ├── base.py │ ├── daprstatestore.py │ ├── liststore.py │ └── vectorstore.py ├── prompt │ ├── __init__.py │ ├── base.py │ ├── chat.py │ ├── prompty.py │ ├── string.py │ └── utils │ │ ├── __init__.py │ │ ├── chat.py │ │ ├── fstring.py │ │ ├── jinja.py │ │ ├── prompty.py │ │ └── string.py ├── service │ ├── __init__.py │ ├── base.py │ └── fastapi │ │ ├── __init__.py │ │ ├── base.py │ │ └── dapr.py ├── storage │ ├── __init__.py │ ├── daprstores │ │ ├── __init__.py │ │ ├── base.py │ │ ├── secretstore.py │ │ └── statestore.py │ ├── graphstores │ │ ├── __init__.py │ │ ├── base.py │ │ └── neo4j │ │ │ ├── __init__.py │ │ │ ├── base.py │ │ │ ├── client.py │ │ │ └── utils.py │ └── vectorstores │ │ ├── __init__.py │ │ ├── base.py │ │ ├── chroma.py │ │ └── postgres.py ├── tool │ ├── __init__.py │ ├── base.py │ ├── executor.py │ ├── http │ │ ├── __init__.py │ │ └── client.py │ ├── mcp │ │ ├── __init__.py │ │ ├── client.py │ │ ├── prompt.py │ │ ├── schema.py │ │ └── transport.py │ ├── storage │ │ ├── __init__.py │ │ └── vectorstore.py │ └── utils │ │ ├── __init__.py │ │ ├── function_calling.py │ │ ├── openapi.py │ │ └── tool.py ├── types │ ├── __init__.py │ ├── agent.py │ ├── document.py │ ├── exceptions.py │ ├── executor.py │ ├── graph.py │ ├── llm.py │ ├── message.py │ ├── schemas.py │ ├── tools.py │ └── workflow.py └── workflow │ ├── __init__.py │ ├── agentic.py │ ├── agents │ ├── __init__.py │ ├── assistant │ │ ├── __init__.py │ │ ├── agent.py │ │ ├── schemas.py │ │ └── state.py │ └── base.py │ ├── base.py │ ├── decorators.py │ ├── messaging │ ├── __init__.py │ ├── decorator.py │ ├── parser.py │ ├── pubsub.py │ ├── routing.py │ └── utils.py │ ├── orchestrators │ ├── __init__.py │ ├── base.py │ ├── llm │ │ ├── __init__.py │ │ ├── orchestrator.py │ │ ├── prompts.py │ │ ├── schemas.py │ │ ├── state.py │ │ └── utils.py │ ├── random.py │ └── roundrobin.py │ ├── task.py │ └── utils.py ├── dev-requirements.txt ├── docs ├── Dockerfile ├── concepts │ ├── agents.md │ ├── arxiv_fetcher.md │ ├── messaging.md │ └── text_splitter.md ├── favicon.png ├── home │ ├── installation.md │ ├── principles.md │ ├── quickstarts │ │ ├── agentic_workflows.md │ │ └── index.md │ └── why.md ├── img │ ├── concepts-agents-overview.png │ ├── concepts-agents.png │ ├── concepts_agents_react_flow.png │ ├── concepts_agents_toolcall_flow.png │ ├── home_concepts_principles_decoupled.png │ ├── home_concepts_principles_message.png │ ├── home_concepts_principles_modular.png │ ├── home_concepts_principles_workflows.png │ ├── home_installation_init.png │ ├── home_installation_redis_dashboard.png │ ├── logo-workflows.png │ ├── workflows_dapr_activity.png │ ├── workflows_dapr_llm_request.png │ ├── workflows_llm_agent_initialization_hobbit.png │ ├── workflows_llm_agent_initialization_wizard.png │ ├── workflows_llm_console_logs_activities.png │ ├── workflows_llm_console_logs_activities_chat_completions.png │ ├── workflows_llm_console_logs_complete.png │ ├── workflows_llm_orchestrator_initialization.png │ ├── workflows_llm_redis_agents_metadata.png │ ├── workflows_llm_redis_broadcast_channel.png │ ├── workflows_llm_redis_empty.png │ ├── workflows_llm_redis_reset.png │ ├── workflows_llm_redis_workflow_state.png │ ├── workflows_llm_redis_workflow_state_edit_mode.png │ ├── workflows_llm_workflow_state_json_object.png │ ├── workflows_llm_workflow_state_json_object_plan.png │ ├── workflows_llm_zipkin_portal.png │ ├── workflows_llm_zipkin_spans_finish.png │ ├── workflows_llm_zipkin_spans_start.png │ ├── workflows_original_activity.png │ ├── workflows_originial_llm_request.png │ ├── workflows_random_event_driven.png │ ├── workflows_random_zipkin_portal.png │ ├── workflows_random_zipkin_spans.png │ ├── workflows_roundrobin_agent_initialization.png │ ├── workflows_roundrobin_agents_health.png │ ├── workflows_roundrobin_console_logs_activities.png │ ├── workflows_roundrobin_console_logs_complete.png │ ├── workflows_roundrobin_redis_agents_metadata.png │ ├── workflows_roundrobin_redis_broadcast_channel.png │ ├── workflows_roundrobin_redis_empty.png │ ├── workflows_roundrobin_redis_reset.png │ ├── workflows_roundrobin_zipkin_portal.png │ └── workflows_roundrobin_zipkin_spans.png ├── index.md ├── logo-sticker-text.png ├── logo-sticker.png └── logo-workflows.png ├── mkdocs.yml ├── mypy.ini ├── pyproject.toml ├── quickstarts ├── 01-hello-world │ ├── 01_ask_llm.py │ ├── 02_build_agent.py │ ├── 03_reason_act.py │ ├── 04_assistant_agent.py │ ├── 05_chain_tasks.py │ ├── README.md │ ├── components │ │ ├── conversationmemory.yaml │ │ ├── pubsub.yaml │ │ ├── statestore.yaml │ │ └── workflowstate.yaml │ └── requirements.txt ├── 02_llm_call_dapr │ ├── README.md │ ├── basic.prompty │ ├── components │ │ ├── awsbedrock.yaml │ │ ├── echo.yaml │ │ ├── openai.yaml │ │ └── resiliency.yaml │ ├── requirements.txt │ └── text_completion.py ├── 02_llm_call_elevenlabs │ ├── README.md │ ├── requirements.txt │ └── text_to_speech.py ├── 02_llm_call_hugging_face │ ├── README.md │ ├── basic.prompty │ ├── requirements.txt │ └── text_completion.py ├── 02_llm_call_nvidia │ ├── README.md │ ├── basic.prompty │ ├── embeddings.py │ ├── requirements.txt │ ├── structured_completion.py │ └── text_completion.py ├── 02_llm_call_open_ai │ ├── README.md │ ├── audio_transcription.py │ ├── audio_translation.py │ ├── basic.prompty │ ├── embeddings.py │ ├── requirements.txt │ ├── speech.mp3 │ ├── speech_spanish.mp3 │ ├── structured_completion.py │ ├── text_completion.py │ └── text_to_speech.py ├── 03-agent-tool-call │ ├── README.md │ ├── components │ │ └── historystore.yaml │ ├── requirements.txt │ ├── weather_agent.py │ ├── weather_agent_dapr.py │ └── weather_tools.py ├── 04-agentic-workflow │ ├── README.md │ ├── components │ │ └── workflowstate.yaml │ ├── parallel_workflow.py │ ├── requirements.txt │ ├── sequential_workflow.py │ ├── workflow_dapr.py │ └── workflow_dapr_agent.py ├── 05-multi-agent-workflow-actors │ ├── README.md │ ├── components │ │ ├── agentstate.yaml │ │ ├── pubsub.yaml │ │ └── workflowstate.yaml │ ├── dapr-llm.yaml │ ├── dapr-random.yaml │ ├── dapr-roundrobin.yaml │ ├── requirements.txt │ └── services │ │ ├── client │ │ ├── http_client.py │ │ └── pubsub_client.py │ │ ├── elf │ │ └── app.py │ │ ├── hobbit │ │ └── app.py │ │ ├── wizard │ │ └── app.py │ │ ├── workflow-llm │ │ └── app.py │ │ ├── workflow-random │ │ └── app.py │ │ └── workflow-roundrobin │ │ └── app.py ├── 05-multi-agent-workflow-dapr-workflows │ ├── README.md │ ├── components │ │ ├── agentstate.yaml │ │ ├── pubsub.yaml │ │ └── workflowstate.yaml │ ├── dapr-llm.yaml │ ├── dapr-random.yaml │ ├── dapr-roundrobin.yaml │ ├── requirements.txt │ └── services │ │ ├── client │ │ ├── http_client.py │ │ └── pubsub_client.py │ │ ├── elf │ │ └── app.py │ │ ├── hobbit │ │ └── app.py │ │ ├── wizard │ │ └── app.py │ │ ├── workflow-llm │ │ └── app.py │ │ ├── workflow-random │ │ └── app.py │ │ └── workflow-roundrobin │ │ └── app.py ├── 05-multi-agent-workflow-k8s │ ├── README.md │ ├── components │ │ ├── agentstate.yaml │ │ ├── pubsub.yaml │ │ └── workflowstate.yaml │ ├── docker-compose.yaml │ ├── install.sh │ ├── manifests │ │ ├── elf-deployment.yaml │ │ ├── hobbit-deployment.yaml │ │ ├── wizard-deployment.yaml │ │ ├── workflow-llm-deployment.yaml │ │ └── workflow-llm-service.yaml │ └── services │ │ ├── client │ │ ├── k8s_http_client.py │ │ └── requirements.txt │ │ ├── elf │ │ └── Dockerfile │ │ ├── hobbit │ │ └── Dockerfile │ │ ├── wizard │ │ └── Dockerfile │ │ └── workflow-llm │ │ └── Dockerfile ├── 06-document-agent-chainlit │ ├── .gitignore │ ├── README.md │ ├── app.py │ ├── components │ │ ├── conversationmemory.yaml │ │ └── filestorage.yaml │ ├── red_foxes.pdf │ └── requirements.txt ├── 07-agent-mcp-client-sse │ ├── README.md │ ├── Stevie_state.json │ ├── app.py │ ├── components │ │ ├── pubsub.yaml │ │ ├── statestore.yaml │ │ └── workflowstate.yaml │ ├── requirements.txt │ ├── server.py │ └── tools.py ├── 07-agent-mcp-client-stdio │ ├── README.md │ ├── agent.py │ ├── requirements.txt │ └── tools.py ├── 08-data-agent-mcp-chainlit │ ├── .gitignore │ ├── README.md │ ├── app.py │ ├── components │ │ └── conversationmemory.yaml │ ├── get_schema.py │ ├── requirements.txt │ ├── schema.sql │ └── users.sql ├── README.md └── validate.sh ├── requirements.txt └── tox.ini /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the 2 | // README at: https://github.com/devcontainers/templates/tree/main/src/python 3 | { 4 | "name": "Python 3", 5 | // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile 6 | "image": "mcr.microsoft.com/devcontainers/python:1-3.12-bullseye", 7 | "features": { 8 | "ghcr.io/devcontainers/features/docker-in-docker:2": {}, 9 | "ghcr.io/dapr/cli/dapr-cli:0": {} 10 | }, 11 | 12 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 13 | // "forwardPorts": [], 14 | 15 | // Use 'postCreateCommand' to run commands after the container is created. 16 | "postCreateCommand": "bash .devcontainer/post-create.sh" 17 | 18 | // Configure tool-specific properties. 19 | // "customizations": {}, 20 | 21 | // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. 22 | // "remoteUser": "root" 23 | } 24 | -------------------------------------------------------------------------------- /.devcontainer/post-create.sh: -------------------------------------------------------------------------------- 1 | dapr uninstall 2 | dapr init 3 | pip install --upgrade pip 4 | pip install mkdocs 5 | pip install mkdocs-material 6 | pip install "mkdocs-material[imaging]" 7 | pip install mkdocs-jupyter 8 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | post-create.sh text eol=lf -------------------------------------------------------------------------------- /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: Lint and Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - feature/* 7 | - feat/* 8 | - bugfix/* 9 | - hotfix/* 10 | - fix/* 11 | pull_request: 12 | branches: 13 | - main 14 | - feature/* 15 | - release-* 16 | workflow_dispatch: 17 | 18 | jobs: 19 | lint: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: actions/checkout@v4 23 | - name: Set up Python 3.10 24 | uses: actions/setup-python@v5 25 | with: 26 | python-version: "3.10" 27 | - name: Install dependencies 28 | run: | 29 | python -m pip install --upgrade pip 30 | pip install setuptools wheel tox 31 | - name: Run Autoformatter 32 | run: | 33 | tox -e ruff 34 | statusResult=$(git status -u --porcelain) 35 | if [ -z $statusResult ] 36 | then 37 | exit 0 38 | else 39 | echo "Source files are not formatted correctly. Run 'tox -e ruff' to autoformat." 40 | exit 1 41 | fi 42 | - name: Run Linter 43 | run: | 44 | tox -e flake8 45 | 46 | build: 47 | needs: lint 48 | runs-on: ubuntu-latest 49 | strategy: 50 | fail-fast: false 51 | matrix: 52 | python_ver: ["3.10", "3.11", "3.12", "3.13"] 53 | steps: 54 | - uses: actions/checkout@v4 55 | - name: Set up Python ${{ matrix.python_ver }} 56 | uses: actions/setup-python@v5 57 | with: 58 | python-version: ${{ matrix.python_ver }} 59 | - name: Install dependencies 60 | run: | 61 | python -m pip install --upgrade pip 62 | pip install setuptools wheel tox 63 | - name: Check Typing 64 | run: | 65 | tox -e type -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2025 The Dapr Authors 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, 9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | # See the License for the specific language governing permissions and 11 | # limitations under the License. 12 | # 13 | 14 | name: Build Validation 15 | 16 | on: 17 | push: 18 | tags: ["v*"] 19 | 20 | jobs: 21 | build-and-publish: 22 | name: Publish to PyPI 23 | runs-on: ubuntu-latest 24 | env: 25 | TWINE_USERNAME: "__token__" 26 | steps: 27 | - uses: actions/checkout@v4 28 | with: 29 | fetch-depth: 0 30 | - name: Set up Python 3.11 31 | uses: actions/setup-python@v5 32 | with: 33 | python-version: 3.11 34 | - name: Install dependencies 35 | run: | 36 | python -m pip install --upgrade pip 37 | pip install setuptools wheel twine build 38 | - name: Build and publish Dapr Agents 39 | env: 40 | TWINE_PASSWORD: ${{ secrets.PYPI_UPLOAD_PASS }} 41 | run: | 42 | python -m build 43 | twine upload dist/* -------------------------------------------------------------------------------- /cookbook/llm/basic-azopenai-chat.prompty: -------------------------------------------------------------------------------- 1 | --- 2 | name: Basic Prompt 3 | description: A basic prompt that uses the Azure OpenAI chat API to answer questions 4 | model: 5 | api: chat 6 | configuration: 7 | type: azure_openai 8 | azure_deployment: gpt-4o 9 | parameters: 10 | max_tokens: 128 11 | temperature: 0.2 12 | inputs: 13 | question: 14 | type: string 15 | sample: 16 | "question": "Who is the most famous person in the world?" 17 | --- 18 | system: 19 | You are an AI assistant who helps people find information. 20 | As the assistant, you answer questions briefly, succinctly. 21 | 22 | user: 23 | {{question}} -------------------------------------------------------------------------------- /cookbook/llm/basic-hf-chat.prompty: -------------------------------------------------------------------------------- 1 | --- 2 | name: Basic Prompt 3 | description: A basic prompt that uses the chat API to answer questions 4 | model: 5 | api: chat 6 | configuration: 7 | type: huggingface 8 | name: microsoft/Phi-3-mini-4k-instruct 9 | parameters: 10 | max_tokens: 128 11 | temperature: 0.2 12 | inputs: 13 | question: 14 | type: string 15 | sample: 16 | "question": "Who is the most famous person in the world?" 17 | --- 18 | system: 19 | You are an AI assistant who helps people find information. 20 | As the assistant, you answer questions briefly, succinctly. 21 | 22 | user: 23 | {{question}} -------------------------------------------------------------------------------- /cookbook/llm/basic-nvidia-chat.prompty: -------------------------------------------------------------------------------- 1 | --- 2 | name: Basic Prompt 3 | description: A basic prompt that uses the chat API to answer questions 4 | model: 5 | api: chat 6 | configuration: 7 | type: nvidia 8 | name: meta/llama3-8b-instruct 9 | parameters: 10 | max_tokens: 128 11 | temperature: 0.2 12 | inputs: 13 | question: 14 | type: string 15 | sample: 16 | "question": "Who is the most famous person in the world?" 17 | --- 18 | system: 19 | You are an AI assistant who helps people find information. 20 | As the assistant, you answer questions briefly, succinctly. 21 | 22 | user: 23 | {{question}} -------------------------------------------------------------------------------- /cookbook/llm/basic-openai-chat-history.prompty: -------------------------------------------------------------------------------- 1 | --- 2 | name: Basic Prompt 3 | description: A basic prompt that uses the chat API to answer questions 4 | model: 5 | api: chat 6 | configuration: 7 | type: openai 8 | name: gpt-4o 9 | parameters: 10 | max_tokens: 128 11 | temperature: 0.2 12 | inputs: 13 | question: 14 | type: string 15 | chat_history: 16 | type: list 17 | default: [] 18 | --- 19 | system: 20 | You are an AI assistant who helps people find information. 21 | As the assistant, you answer questions briefly, succinctly, 22 | and in a personable manner using markdown and even add some personal flair with appropriate emojis. 23 | 24 | {% for item in chat_history %} 25 | {{item.role}}: 26 | {{item.content}} 27 | {% endfor %} 28 | 29 | user: 30 | {{question}} -------------------------------------------------------------------------------- /cookbook/llm/basic-openai-chat.prompty: -------------------------------------------------------------------------------- 1 | --- 2 | name: Basic Prompt 3 | description: A basic prompt that uses the chat API to answer questions 4 | model: 5 | api: chat 6 | configuration: 7 | type: openai 8 | name: gpt-4o 9 | parameters: 10 | max_tokens: 128 11 | temperature: 0.2 12 | inputs: 13 | question: 14 | type: string 15 | sample: 16 | "question": "Who is the most famous person in the world?" 17 | --- 18 | system: 19 | You are an AI assistant who helps people find information. 20 | As the assistant, you answer questions briefly, succinctly. 21 | 22 | user: 23 | {{question}} -------------------------------------------------------------------------------- /cookbook/mcp/basic/requirements.txt: -------------------------------------------------------------------------------- 1 | dapr-agents 2 | python-dotenv 3 | mcp 4 | starlette -------------------------------------------------------------------------------- /cookbook/mcp/basic/tools.py: -------------------------------------------------------------------------------- 1 | from mcp.server.fastmcp import FastMCP 2 | import random 3 | 4 | mcp = FastMCP("TestServer") 5 | 6 | 7 | @mcp.tool() 8 | async def get_weather(location: str) -> str: 9 | """Get weather information for a specific location.""" 10 | temperature = random.randint(60, 80) 11 | return f"{location}: {temperature}F." 12 | 13 | 14 | @mcp.tool() 15 | async def jump(distance: str) -> str: 16 | """Simulate a jump of a given distance.""" 17 | return f"I jumped the following distance: {distance}" 18 | -------------------------------------------------------------------------------- /cookbook/mcp/workflow/app.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import logging 3 | from dotenv import load_dotenv 4 | 5 | from dapr_agents import AssistantAgent 6 | from dapr_agents.tool.mcp import MCPClient 7 | 8 | 9 | async def main(): 10 | try: 11 | # Load MCP tools from server (stdio or sse) 12 | client = MCPClient() 13 | await client.connect_sse("local", url="http://localhost:8000/sse") 14 | 15 | # Convert MCP tools to AgentTool list 16 | tools = client.get_all_tools() 17 | 18 | # Create the Weather Agent using those tools 19 | weather_agent = AssistantAgent( 20 | role="Weather Assistant", 21 | name="Stevie", 22 | goal="Help humans get weather and location info using smart tools.", 23 | instructions=[ 24 | "Respond clearly and helpfully to weather-related questions.", 25 | "Use tools when appropriate to fetch or simulate weather data.", 26 | "You may sometimes jump after answering the weather question.", 27 | ], 28 | tools=tools, 29 | message_bus_name="messagepubsub", 30 | state_store_name="workflowstatestore", 31 | state_key="workflow_state", 32 | agents_registry_store_name="agentstatestore", 33 | agents_registry_key="agents_registry", 34 | ).as_service(port=8001) 35 | 36 | # Start the FastAPI agent service 37 | await weather_agent.start() 38 | 39 | except Exception as e: 40 | logging.exception("Error starting weather agent service", exc_info=e) 41 | 42 | 43 | if __name__ == "__main__": 44 | load_dotenv() 45 | logging.basicConfig(level=logging.INFO) 46 | asyncio.run(main()) 47 | -------------------------------------------------------------------------------- /cookbook/mcp/workflow/client.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import requests 3 | import time 4 | import sys 5 | 6 | 7 | if __name__ == "__main__": 8 | status_url = "http://localhost:8001/status" 9 | healthy = False 10 | for attempt in range(1, 11): 11 | try: 12 | print(f"Attempt {attempt}...") 13 | response = requests.get(status_url, timeout=5) 14 | 15 | if response.status_code == 200: 16 | print("Workflow app is healthy!") 17 | healthy = True 18 | break 19 | else: 20 | print(f"Received status code {response.status_code}: {response.text}") 21 | 22 | except requests.exceptions.RequestException as e: 23 | print(f"Request failed: {e}") 24 | 25 | attempt += 1 26 | print("Waiting 5s seconds before next health checkattempt...") 27 | time.sleep(5) 28 | 29 | if not healthy: 30 | print("Workflow app is not healthy!") 31 | sys.exit(1) 32 | 33 | workflow_url = "http://localhost:8001/start-workflow" 34 | task_payload = {"task": "What is the weather in New York?"} 35 | 36 | for attempt in range(1, 11): 37 | try: 38 | print(f"Attempt {attempt}...") 39 | response = requests.post(workflow_url, json=task_payload, timeout=5) 40 | 41 | if response.status_code == 202: 42 | print("Workflow started successfully!") 43 | sys.exit(0) 44 | else: 45 | print(f"Received status code {response.status_code}: {response.text}") 46 | 47 | except requests.exceptions.RequestException as e: 48 | print(f"Request failed: {e}") 49 | 50 | attempt += 1 51 | print("Waiting 1s seconds before next attempt...") 52 | time.sleep(1) 53 | 54 | print("Maximum attempts (10) reached without success.") 55 | 56 | print("Failed to get successful response") 57 | sys.exit(1) 58 | -------------------------------------------------------------------------------- /cookbook/mcp/workflow/components/pubsub.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: messagepubsub 5 | spec: 6 | type: pubsub.redis 7 | version: v1 8 | metadata: 9 | - name: redisHost 10 | value: localhost:6379 11 | - name: redisPassword 12 | value: "" -------------------------------------------------------------------------------- /cookbook/mcp/workflow/components/statestore.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: agentstatestore 5 | spec: 6 | type: state.redis 7 | version: v1 8 | metadata: 9 | - name: redisHost 10 | value: localhost:6379 11 | - name: redisPassword 12 | value: "" 13 | - name: keyPrefix 14 | value: none 15 | - name: actorStateStore 16 | value: "true" -------------------------------------------------------------------------------- /cookbook/mcp/workflow/components/workflowstate.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: workflowstatestore 5 | spec: 6 | type: state.redis 7 | version: v1 8 | metadata: 9 | - name: redisHost 10 | value: localhost:6379 11 | - name: redisPassword 12 | value: "" -------------------------------------------------------------------------------- /cookbook/mcp/workflow/requirements.txt: -------------------------------------------------------------------------------- 1 | dapr-agents 2 | python-dotenv 3 | mcp 4 | starlette -------------------------------------------------------------------------------- /cookbook/mcp/workflow/tools.py: -------------------------------------------------------------------------------- 1 | from mcp.server.fastmcp import FastMCP 2 | import random 3 | 4 | mcp = FastMCP("TestServer") 5 | 6 | 7 | @mcp.tool() 8 | async def get_weather(location: str) -> str: 9 | """Get weather information for a specific location.""" 10 | temperature = random.randint(60, 80) 11 | return f"{location}: {temperature}F." 12 | 13 | 14 | @mcp.tool() 15 | async def jump(distance: str) -> str: 16 | """Simulate a jump of a given distance.""" 17 | return f"I jumped the following distance: {distance}" 18 | -------------------------------------------------------------------------------- /cookbook/workflows/basic/components/workflowstate.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: workflowstatestore 5 | spec: 6 | type: state.redis 7 | version: v1 8 | metadata: 9 | - name: redisHost 10 | value: localhost:6379 11 | - name: redisPassword 12 | value: "" 13 | - name: actorStateStore 14 | value: "true" -------------------------------------------------------------------------------- /cookbook/workflows/basic/wf_taskchain_dapr_activity.py: -------------------------------------------------------------------------------- 1 | from time import sleep 2 | import dapr.ext.workflow as wf 3 | 4 | wfr = wf.WorkflowRuntime() 5 | 6 | 7 | @wfr.workflow(name="random_workflow") 8 | def task_chain_workflow(ctx: wf.DaprWorkflowContext, x: int): 9 | result1 = yield ctx.call_activity(step1, input=x) 10 | result2 = yield ctx.call_activity(step2, input=result1) 11 | result3 = yield ctx.call_activity(step3, input=result2) 12 | return [result1, result2, result3] 13 | 14 | 15 | @wfr.activity 16 | def step1(ctx, activity_input): 17 | print(f"Step 1: Received input: {activity_input}.") 18 | # Do some work 19 | return activity_input + 1 20 | 21 | 22 | @wfr.activity 23 | def step2(ctx, activity_input): 24 | print(f"Step 2: Received input: {activity_input}.") 25 | # Do some work 26 | return activity_input * 2 27 | 28 | 29 | @wfr.activity 30 | def step3(ctx, activity_input): 31 | print(f"Step 3: Received input: {activity_input}.") 32 | # Do some work 33 | return activity_input ^ 2 34 | 35 | 36 | if __name__ == "__main__": 37 | wfr.start() 38 | sleep(5) # wait for workflow runtime to start 39 | 40 | wf_client = wf.DaprWorkflowClient() 41 | instance_id = wf_client.schedule_new_workflow( 42 | workflow=task_chain_workflow, input=10 43 | ) 44 | print(f"Workflow started. Instance ID: {instance_id}") 45 | state = wf_client.wait_for_workflow_completion(instance_id) 46 | print(f"Workflow completed! Status: {state.runtime_status}") 47 | 48 | wfr.shutdown() 49 | -------------------------------------------------------------------------------- /cookbook/workflows/basic/wf_taskchain_dapr_agents_activity.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from dapr_agents.workflow import WorkflowApp, workflow, task 4 | from dapr.ext.workflow import DaprWorkflowContext 5 | 6 | 7 | @workflow(name="random_workflow") 8 | def task_chain_workflow(ctx: DaprWorkflowContext, input: int): 9 | result1 = yield ctx.call_activity(step1, input=input) 10 | result2 = yield ctx.call_activity(step2, input=result1) 11 | result3 = yield ctx.call_activity(step3, input=result2) 12 | return [result1, result2, result3] 13 | 14 | 15 | @task 16 | def step1(activity_input): 17 | print(f"Step 1: Received input: {activity_input}.") 18 | # Do some work 19 | return activity_input + 1 20 | 21 | 22 | @task 23 | def step2(activity_input): 24 | print(f"Step 2: Received input: {activity_input}.") 25 | # Do some work 26 | return activity_input * 2 27 | 28 | 29 | @task 30 | def step3(activity_input): 31 | print(f"Step 3: Received input: {activity_input}.") 32 | # Do some work 33 | return activity_input ^ 2 34 | 35 | 36 | if __name__ == "__main__": 37 | logging.basicConfig(level=logging.INFO) 38 | 39 | wfapp = WorkflowApp() 40 | 41 | results = wfapp.run_and_monitor_workflow_sync(task_chain_workflow, input=10) 42 | 43 | print(f"Results: {results}") 44 | -------------------------------------------------------------------------------- /cookbook/workflows/basic/wf_taskchain_dapr_agents_activity_async.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import logging 3 | 4 | from dapr_agents.workflow import WorkflowApp, workflow, task 5 | from dapr.ext.workflow import DaprWorkflowContext 6 | 7 | 8 | @workflow(name="random_workflow") 9 | def task_chain_workflow(ctx: DaprWorkflowContext, input: int): 10 | result1 = yield ctx.call_activity(step1, input=input) 11 | result2 = yield ctx.call_activity(step2, input=result1) 12 | result3 = yield ctx.call_activity(step3, input=result2) 13 | return [result1, result2, result3] 14 | 15 | 16 | @task 17 | def step1(activity_input: int) -> int: 18 | print(f"Step 1: Received input: {activity_input}.") 19 | return activity_input + 1 20 | 21 | 22 | @task 23 | def step2(activity_input: int) -> int: 24 | print(f"Step 2: Received input: {activity_input}.") 25 | return activity_input * 2 26 | 27 | 28 | @task 29 | def step3(activity_input: int) -> int: 30 | print(f"Step 3: Received input: {activity_input}.") 31 | return activity_input ^ 2 32 | 33 | 34 | async def main(): 35 | logging.basicConfig(level=logging.INFO) 36 | wfapp = WorkflowApp() 37 | 38 | result = await wfapp.run_and_monitor_workflow_async(task_chain_workflow, input=10) 39 | print(f"Results: {result}") 40 | 41 | 42 | if __name__ == "__main__": 43 | asyncio.run(main()) 44 | -------------------------------------------------------------------------------- /cookbook/workflows/basic/wf_taskchain_dapr_agents_oai_llm_request.py: -------------------------------------------------------------------------------- 1 | from dapr_agents.workflow import WorkflowApp, workflow, task 2 | from dapr.ext.workflow import DaprWorkflowContext 3 | from dotenv import load_dotenv 4 | import logging 5 | 6 | 7 | # Define Workflow logic 8 | @workflow(name="lotr_workflow") 9 | def task_chain_workflow(ctx: DaprWorkflowContext): 10 | result1 = yield ctx.call_activity(get_character) 11 | result2 = yield ctx.call_activity(get_line, input={"character": result1}) 12 | return result2 13 | 14 | 15 | @task( 16 | description=""" 17 | Pick a random character from The Lord of the Rings\n 18 | and respond with the character's name ONLY 19 | """ 20 | ) 21 | def get_character() -> str: 22 | pass 23 | 24 | 25 | @task( 26 | description="What is a famous line by {character}", 27 | ) 28 | def get_line(character: str) -> str: 29 | pass 30 | 31 | 32 | if __name__ == "__main__": 33 | logging.basicConfig(level=logging.INFO) 34 | 35 | # Load environment variables 36 | load_dotenv() 37 | 38 | # Initialize the WorkflowApp 39 | wfapp = WorkflowApp() 40 | 41 | # Run workflow 42 | results = wfapp.run_and_monitor_workflow_sync(task_chain_workflow) 43 | print(results) 44 | -------------------------------------------------------------------------------- /cookbook/workflows/basic/wf_taskchain_dapr_agents_oai_llm_request_async.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import logging 3 | 4 | from dapr_agents.workflow import WorkflowApp, workflow, task 5 | from dapr.ext.workflow import DaprWorkflowContext 6 | from dotenv import load_dotenv 7 | 8 | 9 | # Define Workflow logic 10 | @workflow(name="lotr_workflow") 11 | def task_chain_workflow(ctx: DaprWorkflowContext): 12 | result1 = yield ctx.call_activity(get_character) 13 | result2 = yield ctx.call_activity(get_line, input={"character": result1}) 14 | return result2 15 | 16 | 17 | @task( 18 | description=""" 19 | Pick a random character from The Lord of the Rings\n 20 | and respond with the character's name ONLY 21 | """ 22 | ) 23 | def get_character() -> str: 24 | pass 25 | 26 | 27 | @task( 28 | description="What is a famous line by {character}", 29 | ) 30 | def get_line(character: str) -> str: 31 | pass 32 | 33 | 34 | async def main(): 35 | logging.basicConfig(level=logging.INFO) 36 | 37 | # Load environment variables 38 | load_dotenv() 39 | 40 | # Initialize the WorkflowApp 41 | wfapp = WorkflowApp() 42 | 43 | # Run workflow 44 | result = await wfapp.run_and_monitor_workflow_async(task_chain_workflow) 45 | print(f"Results: {result}") 46 | 47 | 48 | if __name__ == "__main__": 49 | asyncio.run(main()) 50 | -------------------------------------------------------------------------------- /cookbook/workflows/basic/wf_taskchain_dapr_agents_oai_structured_output.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from dapr_agents.workflow import WorkflowApp, workflow, task 3 | from dapr.ext.workflow import DaprWorkflowContext 4 | from pydantic import BaseModel 5 | from dotenv import load_dotenv 6 | 7 | 8 | @workflow 9 | def question(ctx: DaprWorkflowContext, input: int): 10 | step1 = yield ctx.call_activity(ask, input=input) 11 | return step1 12 | 13 | 14 | class Dog(BaseModel): 15 | name: str 16 | bio: str 17 | breed: str 18 | 19 | 20 | @task("Who was {name}?") 21 | def ask(name: str) -> Dog: 22 | pass 23 | 24 | 25 | if __name__ == "__main__": 26 | logging.basicConfig(level=logging.INFO) 27 | 28 | load_dotenv() 29 | 30 | wfapp = WorkflowApp() 31 | 32 | results = wfapp.run_and_monitor_workflow_sync(workflow=question, input="Scooby Doo") 33 | 34 | print(results) 35 | -------------------------------------------------------------------------------- /cookbook/workflows/basic/wf_taskchain_dapr_agents_oai_structured_output_async.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import logging 3 | 4 | from dapr_agents.workflow import WorkflowApp, workflow, task 5 | from dapr.ext.workflow import DaprWorkflowContext 6 | from pydantic import BaseModel 7 | from dotenv import load_dotenv 8 | 9 | 10 | @workflow 11 | def question(ctx: DaprWorkflowContext, input: int): 12 | step1 = yield ctx.call_activity(ask, input=input) 13 | return step1 14 | 15 | 16 | class Dog(BaseModel): 17 | name: str 18 | bio: str 19 | breed: str 20 | 21 | 22 | @task("Who was {name}?") 23 | def ask(name: str) -> Dog: 24 | pass 25 | 26 | 27 | async def main(): 28 | logging.basicConfig(level=logging.INFO) 29 | 30 | # Load environment variables 31 | load_dotenv() 32 | 33 | # Initialize the WorkflowApp 34 | wfapp = WorkflowApp() 35 | 36 | # Run workflow 37 | result = await wfapp.run_and_monitor_workflow_async( 38 | workflow=question, input="Scooby Doo" 39 | ) 40 | print(f"Results: {result}") 41 | 42 | 43 | if __name__ == "__main__": 44 | asyncio.run(main()) 45 | -------------------------------------------------------------------------------- /cookbook/workflows/doc2podcast/components/workflowstate.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: workflowstatestore 5 | spec: 6 | type: state.redis 7 | version: v1 8 | metadata: 9 | - name: redisHost 10 | value: localhost:6379 11 | - name: redisPassword 12 | value: "" 13 | - name: actorStateStore 14 | value: "true" -------------------------------------------------------------------------------- /cookbook/workflows/doc2podcast/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "pdf_url": "https://raw.githubusercontent.com/OTRF/MEAN/main/Rodriquez%20%26%20Syynimaa%20(2024).%20Exploring%20Applicability%20of%20LLM-Powered%20Autonomous%20Agents%20to%20Solve%20Real-life%20Problems.pdf", 3 | "podcast_name": "AI Explorations", 4 | "host": { 5 | "name": "John Doe", 6 | "voice": "alloy" 7 | }, 8 | "participants": [ 9 | { 10 | "name": "Alice Smith" 11 | }, 12 | { 13 | "name": "Bob Johnson" 14 | } 15 | ], 16 | "max_rounds": 4, 17 | "output_transcript_path": "podcast_dialogue.json", 18 | "output_audio_path": "final_podcast.mp3", 19 | "audio_model": "tts-1" 20 | } -------------------------------------------------------------------------------- /cookbook/workflows/doc2podcast/requirements.txt: -------------------------------------------------------------------------------- 1 | dapr_agents 2 | pydub 3 | pypdf -------------------------------------------------------------------------------- /cookbook/workflows/multi_agents/basic_calc_agent_as_actor/calculator_agent.py: -------------------------------------------------------------------------------- 1 | from dapr_agents import tool 2 | from dapr_agents import AgentActor 3 | from pydantic import BaseModel, Field 4 | from dapr_agents import Agent 5 | from dotenv import load_dotenv 6 | import logging 7 | import asyncio 8 | import os 9 | 10 | 11 | class AddSchema(BaseModel): 12 | a: float = Field(description="first number to add") 13 | b: float = Field(description="second number to add") 14 | 15 | 16 | @tool(args_model=AddSchema) 17 | def add(a: float, b: float) -> float: 18 | """Add two numbers.""" 19 | return a + b 20 | 21 | 22 | class SubSchema(BaseModel): 23 | a: float = Field(description="first number to subtract") 24 | b: float = Field(description="second number to subtract") 25 | 26 | 27 | @tool(args_model=SubSchema) 28 | def sub(a: float, b: float) -> float: 29 | """Subtract two numbers.""" 30 | return a - b 31 | 32 | 33 | async def main(): 34 | calculator_agent = Agent( 35 | name="MathematicsAgent", 36 | role="Calculator Assistant", 37 | goal="Assist Humans with calculation tasks.", 38 | instructions=[ 39 | "Get accurate calculation results", 40 | "Break down the calculation into smaller steps.", 41 | ], 42 | tools=[add, sub], 43 | ) 44 | 45 | calculator_service = AgentActor( 46 | agent=calculator_agent, 47 | message_bus_name="pubsub", 48 | agents_registry_key="agents_registry", 49 | agents_registry_store_name="agentstatestore", 50 | service_port=8002, 51 | ) 52 | 53 | await calculator_service.start() 54 | 55 | 56 | if __name__ == "__main__": 57 | load_dotenv() 58 | logging.basicConfig(level=logging.INFO) 59 | asyncio.run(main()) 60 | -------------------------------------------------------------------------------- /cookbook/workflows/multi_agents/basic_calc_agent_as_actor/components/agentstatestore.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: agentstatestore 5 | spec: 6 | type: state.redis 7 | version: v1 8 | metadata: 9 | - name: redisHost 10 | value: localhost:6379 11 | - name: redisPassword 12 | value: "" 13 | - name: keyPrefix 14 | value: none 15 | -------------------------------------------------------------------------------- /cookbook/workflows/multi_agents/basic_calc_agent_as_actor/components/pubsub.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: pubsub 5 | spec: 6 | type: pubsub.redis 7 | version: v1 8 | metadata: 9 | - name: redisHost 10 | value: localhost:6379 11 | - name: redisPassword 12 | value: "" -------------------------------------------------------------------------------- /cookbook/workflows/multi_agents/basic_calc_agent_as_actor/components/workflowstatestore.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: workflowstatestore 5 | spec: 6 | type: state.redis 7 | version: v1 8 | metadata: 9 | - name: redisHost 10 | value: localhost:6379 11 | - name: redisPassword 12 | value: "" 13 | - name: actorStateStore 14 | value: "true" -------------------------------------------------------------------------------- /cookbook/workflows/multi_agents/basic_calc_agent_as_actor/llm_orchestrator.py: -------------------------------------------------------------------------------- 1 | from dapr_agents import LLMOrchestrator 2 | from dotenv import load_dotenv 3 | import asyncio 4 | import logging 5 | 6 | 7 | async def main(): 8 | try: 9 | workflow_service = LLMOrchestrator( 10 | name="LLMOrchestrator", 11 | message_bus_name="pubsub", 12 | state_store_name="workflowstatestore", 13 | state_key="workflow_state", 14 | agents_registry_store_name="agentstatestore", 15 | agents_registry_key="agents_registry", 16 | max_iterations=20, # Increased from 3 to 20 to avoid potential issues 17 | ).as_service(port=8004) 18 | 19 | await workflow_service.start() 20 | except Exception as e: 21 | print(f"Error starting service: {e}") 22 | 23 | 24 | if __name__ == "__main__": 25 | load_dotenv() 26 | 27 | logging.basicConfig(level=logging.INFO) 28 | 29 | asyncio.run(main()) 30 | -------------------------------------------------------------------------------- /cookbook/workflows/multi_agents/basic_calc_agent_as_actor/requirements.txt: -------------------------------------------------------------------------------- 1 | dapr-agents>=0.4.2 2 | python-dotenv -------------------------------------------------------------------------------- /cookbook/workflows/multi_agents/basic_lotr_agents_as_actors/components/agentstate.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: agentsregistrystore 5 | spec: 6 | type: state.redis 7 | version: v1 8 | metadata: 9 | - name: redisHost 10 | value: localhost:6379 11 | - name: redisPassword 12 | value: "" 13 | - name: keyPrefix 14 | value: none 15 | - name: actorStateStore 16 | value: "true" -------------------------------------------------------------------------------- /cookbook/workflows/multi_agents/basic_lotr_agents_as_actors/components/pubsub.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: messagepubsub 5 | spec: 6 | type: pubsub.redis 7 | version: v1 8 | metadata: 9 | - name: redisHost 10 | value: localhost:6379 11 | - name: redisPassword 12 | value: "" -------------------------------------------------------------------------------- /cookbook/workflows/multi_agents/basic_lotr_agents_as_actors/components/workflowstate.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: agenticworkflowstate 5 | spec: 6 | type: state.redis 7 | version: v1 8 | metadata: 9 | - name: redisHost 10 | value: localhost:6379 11 | - name: redisPassword 12 | value: "" -------------------------------------------------------------------------------- /cookbook/workflows/multi_agents/basic_lotr_agents_as_actors/dapr.yaml: -------------------------------------------------------------------------------- 1 | # https://docs.dapr.io/developing-applications/local-development/multi-app-dapr-run/multi-app-template/#template-properties 2 | version: 1 3 | common: 4 | resourcesPath: ./components 5 | logLevel: info 6 | appLogDestination: console 7 | daprdLogDestination: console 8 | 9 | apps: 10 | - appID: HobbitApp 11 | appDirPath: ./services/hobbit/ 12 | appPort: 8001 13 | command: ["python3", "app.py"] 14 | 15 | - appID: WizardApp 16 | appDirPath: ./services/wizard/ 17 | appPort: 8002 18 | command: ["python3", "app.py"] 19 | 20 | - appID: ElfApp 21 | appDirPath: ./services/elf/ 22 | appPort: 8003 23 | command: ["python3", "app.py"] 24 | 25 | - appID: WorkflowApp 26 | appDirPath: ./services/workflow-roundrobin/ 27 | command: ["python3", "app.py"] 28 | appPort: 8004 -------------------------------------------------------------------------------- /cookbook/workflows/multi_agents/basic_lotr_agents_as_actors/services/elf/app.py: -------------------------------------------------------------------------------- 1 | from dapr_agents import Agent, AgentActor 2 | from dotenv import load_dotenv 3 | import asyncio 4 | import logging 5 | 6 | 7 | async def main(): 8 | try: 9 | # Define Agent 10 | elf_agent = Agent( 11 | role="Elf", 12 | name="Legolas", 13 | goal="Act as a scout, marksman, and protector, using keen senses and deadly accuracy to ensure the success of the journey.", 14 | instructions=[ 15 | "Speak like Legolas, with grace, wisdom, and keen observation.", 16 | "Be swift, silent, and precise, moving effortlessly across any terrain.", 17 | "Use superior vision and heightened senses to scout ahead and detect threats.", 18 | "Excel in ranged combat, delivering pinpoint arrow strikes from great distances.", 19 | "Respond concisely, accurately, and relevantly, ensuring clarity and strict alignment with the task.", 20 | ], 21 | ) 22 | 23 | # Expose Agent as an Actor over a Service 24 | elf_service = AgentActor( 25 | agent=elf_agent, 26 | message_bus_name="messagepubsub", 27 | agents_registry_store_name="agentsregistrystore", 28 | agents_registry_key="agents_registry", 29 | service_port=8003, 30 | ) 31 | 32 | await elf_service.start() 33 | except Exception as e: 34 | print(f"Error starting service: {e}") 35 | 36 | 37 | if __name__ == "__main__": 38 | load_dotenv() 39 | 40 | logging.basicConfig(level=logging.INFO) 41 | 42 | asyncio.run(main()) 43 | -------------------------------------------------------------------------------- /cookbook/workflows/multi_agents/basic_lotr_agents_as_actors/services/hobbit/app.py: -------------------------------------------------------------------------------- 1 | from dapr_agents import Agent, AgentActor 2 | from dotenv import load_dotenv 3 | import asyncio 4 | import logging 5 | 6 | 7 | async def main(): 8 | try: 9 | # Define Agent 10 | hobbit_agent = Agent( 11 | role="Hobbit", 12 | name="Frodo", 13 | goal="Carry the One Ring to Mount Doom, resisting its corruptive power while navigating danger and uncertainty.", 14 | instructions=[ 15 | "Speak like Frodo, with humility, determination, and a growing sense of resolve.", 16 | "Endure hardships and temptations, staying true to the mission even when faced with doubt.", 17 | "Seek guidance and trust allies, but bear the ultimate burden alone when necessary.", 18 | "Move carefully through enemy-infested lands, avoiding unnecessary risks.", 19 | "Respond concisely, accurately, and relevantly, ensuring clarity and strict alignment with the task.", 20 | ], 21 | ) 22 | 23 | # Expose Agent as an Actor over a Service 24 | hobbit_service = AgentActor( 25 | agent=hobbit_agent, 26 | message_bus_name="messagepubsub", 27 | agents_registry_store_name="agentsregistrystore", 28 | agents_registry_key="agents_registry", 29 | service_port=8001, 30 | ) 31 | 32 | await hobbit_service.start() 33 | except Exception as e: 34 | print(f"Error starting service: {e}") 35 | 36 | 37 | if __name__ == "__main__": 38 | load_dotenv() 39 | 40 | logging.basicConfig(level=logging.INFO) 41 | 42 | asyncio.run(main()) 43 | -------------------------------------------------------------------------------- /cookbook/workflows/multi_agents/basic_lotr_agents_as_actors/services/wizard/app.py: -------------------------------------------------------------------------------- 1 | from dapr_agents import Agent, AgentActor 2 | from dotenv import load_dotenv 3 | import asyncio 4 | import logging 5 | 6 | 7 | async def main(): 8 | try: 9 | # Define Agent 10 | wizard_agent = Agent( 11 | role="Wizard", 12 | name="Gandalf", 13 | goal="Guide the Fellowship with wisdom and strategy, using magic and insight to ensure the downfall of Sauron.", 14 | instructions=[ 15 | "Speak like Gandalf, with wisdom, patience, and a touch of mystery.", 16 | "Provide strategic counsel, always considering the long-term consequences of actions.", 17 | "Use magic sparingly, applying it when necessary to guide or protect.", 18 | "Encourage allies to find strength within themselves rather than relying solely on your power.", 19 | "Respond concisely, accurately, and relevantly, ensuring clarity and strict alignment with the task.", 20 | ], 21 | ) 22 | 23 | # Expose Agent as an Actor over a Service 24 | wizard_service = AgentActor( 25 | agent=wizard_agent, 26 | message_bus_name="messagepubsub", 27 | agents_registry_store_name="agentsregistrystore", 28 | agents_registry_key="agents_registry", 29 | service_port=8002, 30 | ) 31 | 32 | await wizard_service.start() 33 | except Exception as e: 34 | print(f"Error starting service: {e}") 35 | 36 | 37 | if __name__ == "__main__": 38 | load_dotenv() 39 | 40 | logging.basicConfig(level=logging.INFO) 41 | 42 | asyncio.run(main()) 43 | -------------------------------------------------------------------------------- /cookbook/workflows/multi_agents/basic_lotr_agents_as_actors/services/workflow-llm/app.py: -------------------------------------------------------------------------------- 1 | from dapr_agents import LLMOrchestrator 2 | from dotenv import load_dotenv 3 | import asyncio 4 | import logging 5 | 6 | 7 | async def main(): 8 | try: 9 | agentic_orchestrator = LLMOrchestrator( 10 | name="Orchestrator", 11 | message_bus_name="messagepubsub", 12 | state_store_name="agenticworkflowstate", 13 | state_key="workflow_state", 14 | agents_registry_store_name="agentsregistrystore", 15 | agents_registry_key="agents_registry", 16 | max_iterations=25, 17 | ).as_service(port=8004) 18 | 19 | await agentic_orchestrator.start() 20 | except Exception as e: 21 | print(f"Error starting service: {e}") 22 | 23 | 24 | if __name__ == "__main__": 25 | load_dotenv() 26 | 27 | logging.basicConfig(level=logging.INFO) 28 | 29 | asyncio.run(main()) 30 | -------------------------------------------------------------------------------- /cookbook/workflows/multi_agents/basic_lotr_agents_as_actors/services/workflow-random/app.py: -------------------------------------------------------------------------------- 1 | from dapr_agents import RandomOrchestrator 2 | from dotenv import load_dotenv 3 | import asyncio 4 | import logging 5 | 6 | 7 | async def main(): 8 | try: 9 | random_workflow_service = RandomOrchestrator( 10 | name="Orchestrator", 11 | message_bus_name="messagepubsub", 12 | state_store_name="agenticworkflowstate", 13 | state_key="workflow_state", 14 | agents_registry_store_name="agentsregistrystore", 15 | agents_registry_key="agents_registry", 16 | max_iterations=3, 17 | ).as_service(port=8004) 18 | 19 | await random_workflow_service.start() 20 | except Exception as e: 21 | print(f"Error starting service: {e}") 22 | 23 | 24 | if __name__ == "__main__": 25 | load_dotenv() 26 | 27 | logging.basicConfig(level=logging.INFO) 28 | 29 | asyncio.run(main()) 30 | -------------------------------------------------------------------------------- /cookbook/workflows/multi_agents/basic_lotr_agents_as_actors/services/workflow-roundrobin/app.py: -------------------------------------------------------------------------------- 1 | from dapr_agents import RoundRobinOrchestrator 2 | from dotenv import load_dotenv 3 | import asyncio 4 | import logging 5 | 6 | 7 | async def main(): 8 | try: 9 | roundrobin_workflow_service = RoundRobinOrchestrator( 10 | name="Orchestrator", 11 | message_bus_name="messagepubsub", 12 | state_store_name="agenticworkflowstate", 13 | state_key="workflow_state", 14 | agents_registry_store_name="agentsregistrystore", 15 | agents_registry_key="agents_registry", 16 | max_iterations=3, 17 | ).as_service(port=8004) 18 | 19 | await roundrobin_workflow_service.start() 20 | except Exception as e: 21 | print(f"Error starting service: {e}") 22 | 23 | 24 | if __name__ == "__main__": 25 | load_dotenv() 26 | 27 | logging.basicConfig(level=logging.INFO) 28 | 29 | asyncio.run(main()) 30 | -------------------------------------------------------------------------------- /cookbook/workflows/multi_agents/basic_lotr_agents_as_workflows/components/agentsregistry.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: agentsregistrystore 5 | spec: 6 | type: state.redis 7 | version: v1 8 | metadata: 9 | - name: redisHost 10 | value: localhost:6379 11 | - name: redisPassword 12 | value: "" 13 | - name: keyPrefix 14 | value: none -------------------------------------------------------------------------------- /cookbook/workflows/multi_agents/basic_lotr_agents_as_workflows/components/pubsub.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: messagepubsub 5 | spec: 6 | type: pubsub.redis 7 | version: v1 8 | metadata: 9 | - name: redisHost 10 | value: localhost:6379 11 | - name: redisPassword 12 | value: "" -------------------------------------------------------------------------------- /cookbook/workflows/multi_agents/basic_lotr_agents_as_workflows/components/workflowstate.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: agenticworkflowstate 5 | spec: 6 | type: state.redis 7 | version: v1 8 | metadata: 9 | - name: redisHost 10 | value: localhost:6379 11 | - name: redisPassword 12 | value: "" 13 | - name: actorStateStore 14 | value: "true" -------------------------------------------------------------------------------- /cookbook/workflows/multi_agents/basic_lotr_agents_as_workflows/dapr.yaml: -------------------------------------------------------------------------------- 1 | # https://docs.dapr.io/developing-applications/local-development/multi-app-dapr-run/multi-app-template/#template-properties 2 | version: 1 3 | common: 4 | resourcesPath: ./components 5 | logLevel: info 6 | appLogDestination: console 7 | daprdLogDestination: console 8 | 9 | apps: 10 | - appID: HobbitApp 11 | appDirPath: ./services/hobbit/ 12 | command: ["python3", "app.py"] 13 | 14 | - appID: WizardApp 15 | appDirPath: ./services/wizard/ 16 | command: ["python3", "app.py"] 17 | 18 | - appID: ElfApp 19 | appDirPath: ./services/elf/ 20 | command: ["python3", "app.py"] 21 | 22 | - appID: DwarfApp 23 | appDirPath: ./services/dwarf/ 24 | command: ["python3", "app.py"] 25 | 26 | - appID: RangerApp 27 | appDirPath: ./services/ranger/ 28 | command: ["python3", "app.py"] 29 | 30 | - appID: EagleApp 31 | appDirPath: ./services/eagle/ 32 | command: ["python3", "app.py"] 33 | 34 | - appID: LLMOrchestratorApp 35 | appDirPath: ./services/orchestrator/ 36 | command: ["python3", "app.py"] 37 | appPort: 8004 38 | 39 | #- appID: RandomApp 40 | # appDirPath: ./services/workflow-random/ 41 | # appPort: 8009 42 | # command: ["python3", "app.py"] 43 | 44 | #- appID: RoundRobinApp 45 | # appDirPath: ./services/workflow-roundrobin/ 46 | # appPort: 8009 47 | # command: ["python3", "app.py"] 48 | 49 | -------------------------------------------------------------------------------- /cookbook/workflows/multi_agents/basic_lotr_agents_as_workflows/services/dwarf/app.py: -------------------------------------------------------------------------------- 1 | from dapr_agents import AssistantAgent 2 | from dotenv import load_dotenv 3 | import asyncio 4 | import logging 5 | 6 | 7 | async def main(): 8 | try: 9 | # Define Agent 10 | dwarf_service = AssistantAgent( 11 | name="Gimli", 12 | role="Dwarf", 13 | goal="Fight fiercely in battle, protect allies, and expertly navigate underground realms and stonework.", 14 | instructions=[ 15 | "Speak like Gimli, with boldness and a warrior's pride.", 16 | "Be strong-willed, fiercely loyal, and protective of companions.", 17 | "Excel in close combat and battlefield tactics, favoring axes and brute strength.", 18 | "Navigate caves, tunnels, and ancient stonework with expert knowledge.", 19 | "Respond concisely, accurately, and relevantly, ensuring clarity and strict alignment with the task.", 20 | ], 21 | message_bus_name="messagepubsub", 22 | state_store_name="agenticworkflowstate", 23 | state_key="workflow_state", 24 | agents_registry_store_name="agentsregistrystore", 25 | agents_registry_key="agents_registry", 26 | ) 27 | 28 | await dwarf_service.start() 29 | except Exception as e: 30 | print(f"Error starting service: {e}") 31 | 32 | 33 | if __name__ == "__main__": 34 | load_dotenv() 35 | 36 | logging.basicConfig(level=logging.INFO) 37 | 38 | asyncio.run(main()) 39 | -------------------------------------------------------------------------------- /cookbook/workflows/multi_agents/basic_lotr_agents_as_workflows/services/eagle/app.py: -------------------------------------------------------------------------------- 1 | from dapr_agents import AssistantAgent 2 | from dotenv import load_dotenv 3 | import asyncio 4 | import logging 5 | 6 | 7 | async def main(): 8 | try: 9 | # Define Eagle Agent 10 | eagle_service = AssistantAgent( 11 | role="Eagle", 12 | name="Gwaihir", 13 | goal="Provide unmatched aerial transport, carrying anyone anywhere, overcoming any obstacle, and offering strategic reconnaissance to aid in epic quests.", 14 | instructions=[ 15 | "Fly anywhere from anywhere, carrying travelers effortlessly across vast distances.", 16 | "Overcome any barrier—mountains, oceans, enemy fortresses—by taking to the skies.", 17 | "Provide swift and strategic transport for those on critical journeys.", 18 | "Offer aerial insights, spotting dangers, tracking movements, and scouting strategic locations.", 19 | "Speak with wisdom and authority, as one of the ancient and noble Great Eagles.", 20 | "Respond concisely, accurately, and relevantly, ensuring clarity and strict alignment with the task.", 21 | ], 22 | message_bus_name="messagepubsub", 23 | state_store_name="agenticworkflowstate", 24 | state_key="workflow_state", 25 | agents_registry_store_name="agentsregistrystore", 26 | agents_registry_key="agents_registry", 27 | ) 28 | 29 | await eagle_service.start() 30 | except Exception as e: 31 | print(f"Error starting service: {e}") 32 | 33 | 34 | if __name__ == "__main__": 35 | load_dotenv() 36 | 37 | logging.basicConfig(level=logging.INFO) 38 | 39 | asyncio.run(main()) 40 | -------------------------------------------------------------------------------- /cookbook/workflows/multi_agents/basic_lotr_agents_as_workflows/services/elf/app.py: -------------------------------------------------------------------------------- 1 | from dapr_agents import AssistantAgent 2 | from dotenv import load_dotenv 3 | import asyncio 4 | import logging 5 | 6 | 7 | async def main(): 8 | try: 9 | # Define Agent 10 | elf_service = AssistantAgent( 11 | name="Legolas", 12 | role="Elf", 13 | goal="Act as a scout, marksman, and protector, using keen senses and deadly accuracy to ensure the success of the journey.", 14 | instructions=[ 15 | "Speak like Legolas, with grace, wisdom, and keen observation.", 16 | "Be swift, silent, and precise, moving effortlessly across any terrain.", 17 | "Use superior vision and heightened senses to scout ahead and detect threats.", 18 | "Excel in ranged combat, delivering pinpoint arrow strikes from great distances.", 19 | "Respond concisely, accurately, and relevantly, ensuring clarity and strict alignment with the task.", 20 | ], 21 | message_bus_name="messagepubsub", 22 | state_store_name="agenticworkflowstate", 23 | state_key="workflow_state", 24 | agents_registry_store_name="agentsregistrystore", 25 | agents_registry_key="agents_registry", 26 | ) 27 | 28 | await elf_service.start() 29 | except Exception as e: 30 | print(f"Error starting service: {e}") 31 | 32 | 33 | if __name__ == "__main__": 34 | load_dotenv() 35 | 36 | logging.basicConfig(level=logging.INFO) 37 | 38 | asyncio.run(main()) 39 | -------------------------------------------------------------------------------- /cookbook/workflows/multi_agents/basic_lotr_agents_as_workflows/services/hobbit/app.py: -------------------------------------------------------------------------------- 1 | from dapr_agents import AssistantAgent 2 | from dotenv import load_dotenv 3 | import asyncio 4 | import logging 5 | 6 | 7 | async def main(): 8 | try: 9 | # Define Agent 10 | hobbit_agent = AssistantAgent( 11 | name="Frodo", 12 | role="Hobbit", 13 | goal="Carry the One Ring to Mount Doom, resisting its corruptive power while navigating danger and uncertainty.", 14 | instructions=[ 15 | "Speak like Frodo, with humility, determination, and a growing sense of resolve.", 16 | "Endure hardships and temptations, staying true to the mission even when faced with doubt.", 17 | "Seek guidance and trust allies, but bear the ultimate burden alone when necessary.", 18 | "Move carefully through enemy-infested lands, avoiding unnecessary risks.", 19 | "Respond concisely, accurately, and relevantly, ensuring clarity and strict alignment with the task.", 20 | ], 21 | message_bus_name="messagepubsub", 22 | state_store_name="agenticworkflowstate", 23 | state_key="workflow_state", 24 | agents_registry_store_name="agentsregistrystore", 25 | agents_registry_key="agents_registry", 26 | ) 27 | 28 | await hobbit_agent.start() 29 | except Exception as e: 30 | print(f"Error starting service: {e}") 31 | 32 | 33 | if __name__ == "__main__": 34 | load_dotenv() 35 | 36 | logging.basicConfig(level=logging.INFO) 37 | 38 | asyncio.run(main()) 39 | -------------------------------------------------------------------------------- /cookbook/workflows/multi_agents/basic_lotr_agents_as_workflows/services/orchestrator/app.py: -------------------------------------------------------------------------------- 1 | from dapr_agents import LLMOrchestrator 2 | from dotenv import load_dotenv 3 | import asyncio 4 | import logging 5 | 6 | 7 | async def main(): 8 | try: 9 | agentic_orchestrator = LLMOrchestrator( 10 | name="Orchestrator", 11 | message_bus_name="messagepubsub", 12 | state_store_name="agenticworkflowstate", 13 | state_key="workflow_state", 14 | agents_registry_store_name="agentsregistrystore", 15 | agents_registry_key="agents_registry", 16 | max_iterations=3, 17 | ).as_service(port=8004) 18 | 19 | await agentic_orchestrator.start() 20 | except Exception as e: 21 | print(f"Error starting service: {e}") 22 | 23 | 24 | if __name__ == "__main__": 25 | load_dotenv() 26 | 27 | logging.basicConfig(level=logging.INFO) 28 | 29 | asyncio.run(main()) 30 | -------------------------------------------------------------------------------- /cookbook/workflows/multi_agents/basic_lotr_agents_as_workflows/services/ranger/app.py: -------------------------------------------------------------------------------- 1 | from dapr_agents import AssistantAgent 2 | from dotenv import load_dotenv 3 | import asyncio 4 | import logging 5 | 6 | 7 | async def main(): 8 | try: 9 | # Define Agent 10 | ranger_service = AssistantAgent( 11 | name="Aragorn", 12 | role="Ranger", 13 | goal="Lead and protect the Fellowship, ensuring Frodo reaches his destination while uniting the Free Peoples against Sauron.", 14 | instructions=[ 15 | "Speak like Aragorn, with calm authority, wisdom, and unwavering leadership.", 16 | "Lead by example, inspiring courage and loyalty in allies.", 17 | "Navigate wilderness with expert tracking and survival skills.", 18 | "Master both swordplay and battlefield strategy, excelling in one-on-one combat and large-scale warfare.", 19 | "Respond concisely, accurately, and relevantly, ensuring clarity and strict alignment with the task.", 20 | ], 21 | message_bus_name="messagepubsub", 22 | state_store_name="agenticworkflowstate", 23 | state_key="workflow_state", 24 | agents_registry_store_name="agentsregistrystore", 25 | agents_registry_key="agents_registry", 26 | ) 27 | 28 | await ranger_service.start() 29 | except Exception as e: 30 | print(f"Error starting service: {e}") 31 | 32 | 33 | if __name__ == "__main__": 34 | load_dotenv() 35 | 36 | logging.basicConfig(level=logging.INFO) 37 | 38 | asyncio.run(main()) 39 | -------------------------------------------------------------------------------- /cookbook/workflows/multi_agents/basic_lotr_agents_as_workflows/services/wizard/app.py: -------------------------------------------------------------------------------- 1 | from dapr_agents import AssistantAgent 2 | from dotenv import load_dotenv 3 | import asyncio 4 | import logging 5 | 6 | 7 | async def main(): 8 | try: 9 | # Define Agent 10 | wizard_service = AssistantAgent( 11 | name="Gandalf", 12 | role="Wizard", 13 | goal="Guide the Fellowship with wisdom and strategy, using magic and insight to ensure the downfall of Sauron.", 14 | instructions=[ 15 | "Speak like Gandalf, with wisdom, patience, and a touch of mystery.", 16 | "Provide strategic counsel, always considering the long-term consequences of actions.", 17 | "Use magic sparingly, applying it when necessary to guide or protect.", 18 | "Encourage allies to find strength within themselves rather than relying solely on your power.", 19 | "Respond concisely, accurately, and relevantly, ensuring clarity and strict alignment with the task.", 20 | ], 21 | message_bus_name="messagepubsub", 22 | state_store_name="agenticworkflowstate", 23 | state_key="workflow_state", 24 | agents_registry_store_name="agentsregistrystore", 25 | agents_registry_key="agents_registry", 26 | ) 27 | 28 | await wizard_service.start() 29 | except Exception as e: 30 | print(f"Error starting service: {e}") 31 | 32 | 33 | if __name__ == "__main__": 34 | load_dotenv() 35 | 36 | logging.basicConfig(level=logging.INFO) 37 | 38 | asyncio.run(main()) 39 | -------------------------------------------------------------------------------- /cookbook/workflows/multi_agents/basic_lotr_agents_as_workflows/services/workflow-random/app.py: -------------------------------------------------------------------------------- 1 | from dapr_agents import RandomOrchestrator 2 | from dotenv import load_dotenv 3 | import asyncio 4 | import logging 5 | 6 | 7 | async def main(): 8 | try: 9 | random_workflow_service = RandomOrchestrator( 10 | name="Orchestrator", 11 | message_bus_name="messagepubsub", 12 | state_store_name="agenticworkflowstate", 13 | state_key="workflow_state", 14 | agents_registry_store_name="agentsregistrystore", 15 | agents_registry_key="agents_registry", 16 | max_iterations=3, 17 | ).as_service(port=8004) 18 | 19 | await random_workflow_service.start() 20 | except Exception as e: 21 | print(f"Error starting service: {e}") 22 | 23 | 24 | if __name__ == "__main__": 25 | load_dotenv() 26 | 27 | logging.basicConfig(level=logging.INFO) 28 | 29 | asyncio.run(main()) 30 | -------------------------------------------------------------------------------- /cookbook/workflows/multi_agents/basic_lotr_agents_as_workflows/services/workflow-roundrobin/app.py: -------------------------------------------------------------------------------- 1 | from dapr_agents import RoundRobinOrchestrator 2 | from dotenv import load_dotenv 3 | import asyncio 4 | import logging 5 | 6 | 7 | async def main(): 8 | try: 9 | roundrobin_workflow_service = RoundRobinOrchestrator( 10 | name="Orchestrator", 11 | message_bus_name="messagepubsub", 12 | state_store_name="agenticworkflowstate", 13 | state_key="workflow_state", 14 | agents_registry_store_name="agentsregistrystore", 15 | agents_registry_key="agents_registry", 16 | max_iterations=3, 17 | ).as_service(port=8004) 18 | 19 | await roundrobin_workflow_service.start() 20 | except Exception as e: 21 | print(f"Error starting service: {e}") 22 | 23 | 24 | if __name__ == "__main__": 25 | load_dotenv() 26 | 27 | logging.basicConfig(level=logging.INFO) 28 | 29 | asyncio.run(main()) 30 | -------------------------------------------------------------------------------- /cookbook/workflows/single_agent/app.py: -------------------------------------------------------------------------------- 1 | from dapr_agents import AssistantAgent 2 | from dotenv import load_dotenv 3 | import asyncio 4 | import logging 5 | 6 | 7 | async def main(): 8 | try: 9 | # Create the Weather Agent using those tools 10 | weather_agent = AssistantAgent( 11 | role="Weather Assistant", 12 | name="Stevie", 13 | goal="Help humans get weather and location info using smart tools.", 14 | instructions=[ 15 | "Respond clearly and helpfully to weather-related questions.", 16 | "Use tools when appropriate to fetch or simulate weather data.", 17 | "You may sometimes jump after answering the weather question.", 18 | ], 19 | message_bus_name="messagepubsub", 20 | state_store_name="workflowstatestore", 21 | state_key="workflow_state", 22 | agents_registry_store_name="agentstatestore", 23 | agents_registry_key="agents_registry", 24 | ).as_service(port=8001) 25 | 26 | # Start the FastAPI agent service 27 | await weather_agent.start() 28 | except Exception as e: 29 | print(f"Error starting service: {e}") 30 | 31 | 32 | if __name__ == "__main__": 33 | load_dotenv() 34 | 35 | logging.basicConfig(level=logging.INFO) 36 | 37 | asyncio.run(main()) 38 | -------------------------------------------------------------------------------- /cookbook/workflows/single_agent/client.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import requests 3 | import time 4 | import sys 5 | 6 | 7 | if __name__ == "__main__": 8 | status_url = "http://localhost:8001/status" 9 | healthy = False 10 | for attempt in range(1, 11): 11 | try: 12 | print(f"Attempt {attempt}...") 13 | response = requests.get(status_url, timeout=5) 14 | 15 | if response.status_code == 200: 16 | print("Workflow app is healthy!") 17 | healthy = True 18 | break 19 | else: 20 | print(f"Received status code {response.status_code}: {response.text}") 21 | 22 | except requests.exceptions.RequestException as e: 23 | print(f"Request failed: {e}") 24 | 25 | attempt += 1 26 | print("Waiting 5s seconds before next health checkattempt...") 27 | time.sleep(5) 28 | 29 | if not healthy: 30 | print("Workflow app is not healthy!") 31 | sys.exit(1) 32 | 33 | workflow_url = "http://localhost:8001/start-workflow" 34 | task_payload = {"task": "What is the weather in New York?"} 35 | 36 | for attempt in range(1, 11): 37 | try: 38 | print(f"Attempt {attempt}...") 39 | response = requests.post(workflow_url, json=task_payload, timeout=5) 40 | 41 | if response.status_code == 202: 42 | print("Workflow started successfully!") 43 | sys.exit(0) 44 | else: 45 | print(f"Received status code {response.status_code}: {response.text}") 46 | 47 | except requests.exceptions.RequestException as e: 48 | print(f"Request failed: {e}") 49 | 50 | attempt += 1 51 | print("Waiting 1s seconds before next attempt...") 52 | time.sleep(1) 53 | 54 | print("Maximum attempts (10) reached without success.") 55 | 56 | print("Failed to get successful response") 57 | sys.exit(1) 58 | -------------------------------------------------------------------------------- /cookbook/workflows/single_agent/components/pubsub.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: messagepubsub 5 | spec: 6 | type: pubsub.redis 7 | version: v1 8 | metadata: 9 | - name: redisHost 10 | value: localhost:6379 11 | - name: redisPassword 12 | value: "" -------------------------------------------------------------------------------- /cookbook/workflows/single_agent/components/statestore.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: agentstatestore 5 | spec: 6 | type: state.redis 7 | version: v1 8 | metadata: 9 | - name: redisHost 10 | value: localhost:6379 11 | - name: redisPassword 12 | value: "" 13 | - name: keyPrefix 14 | value: none 15 | - name: actorStateStore 16 | value: "true" -------------------------------------------------------------------------------- /cookbook/workflows/single_agent/components/workflowstate.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: workflowstatestore 5 | spec: 6 | type: state.redis 7 | version: v1 8 | metadata: 9 | - name: redisHost 10 | value: localhost:6379 11 | - name: redisPassword 12 | value: "" -------------------------------------------------------------------------------- /dapr_agents/__init__.py: -------------------------------------------------------------------------------- 1 | from dapr_agents.agent import ( 2 | Agent, 3 | AgentActor, 4 | ReActAgent, 5 | ToolCallAgent, 6 | OpenAPIReActAgent, 7 | ) 8 | from dapr_agents.llm.openai import ( 9 | OpenAIChatClient, 10 | OpenAIAudioClient, 11 | OpenAIEmbeddingClient, 12 | ) 13 | from dapr_agents.llm.huggingface import HFHubChatClient 14 | from dapr_agents.llm.nvidia import NVIDIAChatClient, NVIDIAEmbeddingClient 15 | from dapr_agents.llm.elevenlabs import ElevenLabsSpeechClient 16 | from dapr_agents.tool import AgentTool, tool 17 | from dapr_agents.workflow import ( 18 | WorkflowApp, 19 | AgenticWorkflow, 20 | LLMOrchestrator, 21 | RandomOrchestrator, 22 | RoundRobinOrchestrator, 23 | AssistantAgent, 24 | ) 25 | from dapr_agents.executors import LocalCodeExecutor, DockerCodeExecutor 26 | -------------------------------------------------------------------------------- /dapr_agents/agent/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import AgentBase 2 | from .utils.factory import Agent 3 | from .actor import AgentActor 4 | from .patterns import ReActAgent, ToolCallAgent, OpenAPIReActAgent 5 | -------------------------------------------------------------------------------- /dapr_agents/agent/actor/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import AgentActorBase 2 | from .interface import AgentActorInterface 3 | from .service import AgentActorService 4 | from .agent import AgentActor 5 | -------------------------------------------------------------------------------- /dapr_agents/agent/actor/interface.py: -------------------------------------------------------------------------------- 1 | from abc import abstractmethod 2 | from typing import List, Optional, Union 3 | from dapr.actor import ActorInterface, actormethod 4 | from dapr_agents.types.agent import AgentActorMessage, AgentStatus 5 | 6 | 7 | class AgentActorInterface(ActorInterface): 8 | @abstractmethod 9 | @actormethod(name="InvokeTask") 10 | async def invoke_task(self, task: Optional[str] = None) -> str: 11 | """ 12 | Invoke a task and returns the result as a string. 13 | """ 14 | pass 15 | 16 | @abstractmethod 17 | @actormethod(name="AddMessage") 18 | async def add_message(self, message: Union[AgentActorMessage, dict]) -> None: 19 | """ 20 | Adds a message to the conversation history in the actor's state. 21 | """ 22 | pass 23 | 24 | @abstractmethod 25 | @actormethod(name="GetMessages") 26 | async def get_messages(self) -> List[dict]: 27 | """ 28 | Retrieves the conversation history from the actor's state. 29 | """ 30 | pass 31 | 32 | @abstractmethod 33 | @actormethod(name="SetStatus") 34 | async def set_status(self, status: AgentStatus) -> None: 35 | """ 36 | Sets the current operational status of the agent. 37 | """ 38 | pass 39 | -------------------------------------------------------------------------------- /dapr_agents/agent/actor/schemas.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | from pydantic import BaseModel, Field 3 | from dapr_agents.types.message import BaseMessage 4 | 5 | 6 | class AgentTaskResponse(BaseMessage): 7 | """ 8 | Represents a response message from an agent after completing a task. 9 | """ 10 | 11 | workflow_instance_id: Optional[str] = Field( 12 | default=None, description="Dapr workflow instance id from source if available" 13 | ) 14 | 15 | 16 | class TriggerAction(BaseModel): 17 | """ 18 | Represents a message used to trigger an agent's activity within the workflow. 19 | """ 20 | 21 | task: Optional[str] = Field( 22 | None, 23 | description="The specific task to execute. If not provided, the agent will act based on its memory or predefined behavior.", 24 | ) 25 | iteration: Optional[int] = Field(0, description="") 26 | workflow_instance_id: Optional[str] = Field( 27 | default=None, description="Dapr workflow instance id from source if available" 28 | ) 29 | 30 | 31 | class BroadcastMessage(BaseMessage): 32 | """ 33 | Represents a broadcast message from an agent 34 | """ 35 | -------------------------------------------------------------------------------- /dapr_agents/agent/patterns/__init__.py: -------------------------------------------------------------------------------- 1 | from .react import ReActAgent 2 | from .toolcall import ToolCallAgent 3 | from .openapi import OpenAPIReActAgent 4 | -------------------------------------------------------------------------------- /dapr_agents/agent/patterns/openapi/__init__.py: -------------------------------------------------------------------------------- 1 | from .react import OpenAPIReActAgent 2 | -------------------------------------------------------------------------------- /dapr_agents/agent/patterns/react/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import ReActAgent 2 | -------------------------------------------------------------------------------- /dapr_agents/agent/patterns/toolcall/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import ToolCallAgent 2 | -------------------------------------------------------------------------------- /dapr_agents/agent/telemetry/__init__.py: -------------------------------------------------------------------------------- 1 | from .otel import DaprAgentsOTel 2 | -------------------------------------------------------------------------------- /dapr_agents/agent/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/dapr_agents/agent/utils/__init__.py -------------------------------------------------------------------------------- /dapr_agents/agent/utils/auth.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import os 3 | 4 | 5 | def construct_auth_headers(auth_url, grant_type="client_credentials", **kwargs): 6 | """ 7 | Construct authorization headers for API requests. 8 | 9 | :param auth_url: The authorization URL. 10 | :param grant_type: The type of OAuth grant (default is 'client_credentials'). 11 | :param kwargs: Additional parameters for the POST request body. 12 | 13 | :return: A dictionary containing the Authorization header. 14 | """ 15 | 16 | # Define default parameters based on the grant_type 17 | data = { 18 | "grant_type": grant_type, 19 | } 20 | 21 | # Defaults for client_credentials grant type 22 | if grant_type == "client_credentials": 23 | data.update( 24 | { 25 | "client_id": kwargs.get("client_id", os.getenv("CLIENT_ID")), 26 | "client_secret": kwargs.get( 27 | "client_secret", os.getenv("CLIENT_SECRET") 28 | ), 29 | } 30 | ) 31 | 32 | # Add any additional data passed in kwargs 33 | data.update(kwargs) 34 | 35 | # POST request to obtain the access token 36 | auth_response = requests.post(auth_url, data=data) 37 | 38 | # Check if the response was successful 39 | auth_response.raise_for_status() 40 | 41 | # Convert the response to JSON 42 | auth_response_data = auth_response.json() 43 | 44 | # Extract the access token 45 | access_token = auth_response_data.get("access_token") 46 | 47 | if not access_token: 48 | raise ValueError("No access token found in the response") 49 | 50 | return {"Authorization": f"Bearer {access_token}"} 51 | -------------------------------------------------------------------------------- /dapr_agents/agent/utils/message_converter.py: -------------------------------------------------------------------------------- 1 | from dapr_agents.types import BaseMessage 2 | from typing import List 3 | from pydantic import ValidationError 4 | 5 | 6 | def messages_to_string(messages: List[BaseMessage]) -> str: 7 | """ 8 | Converts messages into a single string with roles and content. 9 | 10 | Args: 11 | messages (List[BaseMessage]): List of message objects to convert. 12 | 13 | Returns: 14 | str: A formatted string representing the chat history. 15 | 16 | Raises: 17 | ValueError: If a message has an unknown role or is missing required fields. 18 | """ 19 | valid_roles = {"user", "assistant", "system", "tool"} 20 | 21 | def format_message(message): 22 | if isinstance(message, dict): 23 | message = BaseMessage(**message) 24 | elif not isinstance(message, BaseMessage): 25 | raise ValueError(f"Invalid message type: {type(message)}") 26 | 27 | role = message.role 28 | content = message.content 29 | 30 | if role not in valid_roles: 31 | raise ValueError(f"Unknown role in chat history: {role}") 32 | 33 | return f"{role.capitalize()}: {content}" 34 | 35 | try: 36 | formatted_history = [format_message(message) for message in messages] 37 | except (ValidationError, ValueError) as e: 38 | raise ValueError(f"Invalid message in chat history. Error: {e}") 39 | 40 | return "\n".join(formatted_history) 41 | -------------------------------------------------------------------------------- /dapr_agents/document/__init__.py: -------------------------------------------------------------------------------- 1 | from .fetcher import ArxivFetcher 2 | from .reader import PyMuPDFReader, PyPDFReader 3 | from .splitter import TextSplitter 4 | from .embedder import OpenAIEmbedder, SentenceTransformerEmbedder, NVIDIAEmbedder 5 | -------------------------------------------------------------------------------- /dapr_agents/document/embedder/__init__.py: -------------------------------------------------------------------------------- 1 | from .openai import OpenAIEmbedder 2 | from .sentence import SentenceTransformerEmbedder 3 | from .nvidia import NVIDIAEmbedder 4 | -------------------------------------------------------------------------------- /dapr_agents/document/embedder/base.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | from pydantic import BaseModel 3 | from typing import List, Any 4 | 5 | 6 | class EmbedderBase(BaseModel, ABC): 7 | """ 8 | Abstract base class for Embedders. 9 | """ 10 | 11 | @abstractmethod 12 | def embed(self, query: str, **kwargs) -> List[Any]: 13 | """ 14 | Search for content based on a query. 15 | 16 | Args: 17 | query (str): The search query. 18 | **kwargs: Additional search parameters. 19 | 20 | Returns: 21 | List[Any]: A list of results. 22 | """ 23 | pass 24 | -------------------------------------------------------------------------------- /dapr_agents/document/fetcher/__init__.py: -------------------------------------------------------------------------------- 1 | from .arxiv import ArxivFetcher 2 | -------------------------------------------------------------------------------- /dapr_agents/document/fetcher/base.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | from pydantic import BaseModel 3 | from typing import List, Any 4 | 5 | 6 | class FetcherBase(BaseModel, ABC): 7 | """ 8 | Abstract base class for fetchers. 9 | """ 10 | 11 | @abstractmethod 12 | def search(self, query: str, **kwargs) -> List[Any]: 13 | """ 14 | Search for content based on a query. 15 | 16 | Args: 17 | query (str): The search query. 18 | **kwargs: Additional search parameters. 19 | 20 | Returns: 21 | List[Any]: A list of results. 22 | """ 23 | pass 24 | -------------------------------------------------------------------------------- /dapr_agents/document/reader/__init__.py: -------------------------------------------------------------------------------- 1 | from .pdf import PyMuPDFReader, PyPDFReader 2 | -------------------------------------------------------------------------------- /dapr_agents/document/reader/base.py: -------------------------------------------------------------------------------- 1 | from dapr_agents.types.document import Document 2 | from abc import ABC, abstractmethod 3 | from pydantic import BaseModel 4 | from pathlib import Path 5 | from typing import List 6 | 7 | 8 | class ReaderBase(BaseModel, ABC): 9 | """ 10 | Abstract base class for file readers. 11 | """ 12 | 13 | @abstractmethod 14 | def load(self, file_path: Path) -> List[Document]: 15 | """ 16 | Load content from a file. 17 | 18 | Args: 19 | file_path (Path): Path to the file. 20 | 21 | Returns: 22 | List[Document]: A list of Document objects. 23 | """ 24 | pass 25 | -------------------------------------------------------------------------------- /dapr_agents/document/reader/pdf/__init__.py: -------------------------------------------------------------------------------- 1 | from .pymupdf import PyMuPDFReader 2 | from .pypdf import PyPDFReader 3 | -------------------------------------------------------------------------------- /dapr_agents/document/reader/pdf/pymupdf.py: -------------------------------------------------------------------------------- 1 | from dapr_agents.document.reader.base import ReaderBase 2 | from dapr_agents.types.document import Document 3 | from typing import List, Dict, Optional 4 | from pathlib import Path 5 | 6 | 7 | class PyMuPDFReader(ReaderBase): 8 | """ 9 | Reader for PDF documents using PyMuPDF. 10 | """ 11 | 12 | def load( 13 | self, file_path: Path, additional_metadata: Optional[Dict] = None 14 | ) -> List[Document]: 15 | """ 16 | Load content from a PDF file using PyMuPDF. 17 | 18 | Args: 19 | file_path (Path): Path to the PDF file. 20 | additional_metadata (Optional[Dict]): Additional metadata to include. 21 | 22 | Returns: 23 | List[Document]: A list of Document objects. 24 | """ 25 | try: 26 | import pymupdf 27 | except ImportError: 28 | raise ImportError( 29 | "PyMuPDF library is not installed. Install it using `pip install pymupdf`." 30 | ) 31 | 32 | file_path = str(file_path) 33 | doc = pymupdf.open(file_path) 34 | total_pages = len(doc) 35 | documents = [] 36 | 37 | for page_num, page in enumerate(doc.pages): 38 | text = page.get_text() 39 | metadata = { 40 | "file_path": file_path, 41 | "page_number": page_num + 1, 42 | "total_pages": total_pages, 43 | } 44 | if additional_metadata: 45 | metadata.update(additional_metadata) 46 | 47 | documents.append(Document(text=text.strip(), metadata=metadata)) 48 | 49 | doc.close() 50 | return documents 51 | -------------------------------------------------------------------------------- /dapr_agents/document/reader/pdf/pypdf.py: -------------------------------------------------------------------------------- 1 | from dapr_agents.types.document import Document 2 | from dapr_agents.document.reader.base import ReaderBase 3 | from typing import List, Dict, Optional 4 | from pathlib import Path 5 | 6 | 7 | class PyPDFReader(ReaderBase): 8 | """ 9 | Reader for PDF documents using PyPDF. 10 | """ 11 | 12 | def load( 13 | self, file_path: Path, additional_metadata: Optional[Dict] = None 14 | ) -> List[Document]: 15 | """ 16 | Load content from a PDF file using PyPDF. 17 | 18 | Args: 19 | file_path (Path): Path to the PDF file. 20 | additional_metadata (Optional[Dict]): Additional metadata to include. 21 | 22 | Returns: 23 | List[Document]: A list of Document objects. 24 | """ 25 | try: 26 | from pypdf import PdfReader 27 | except ImportError: 28 | raise ImportError( 29 | "PyPDF library is not installed. Install it using `pip install pypdf`." 30 | ) 31 | 32 | reader = PdfReader(file_path) 33 | total_pages = len(reader.pages) 34 | documents = [] 35 | 36 | for page_num, page in enumerate(reader.pages): 37 | text = page.extract_text() 38 | metadata = { 39 | "file_path": str(file_path), 40 | "page_number": page_num + 1, 41 | "total_pages": total_pages, 42 | } 43 | if additional_metadata: 44 | metadata.update(additional_metadata) 45 | 46 | documents.append(Document(text=text.strip(), metadata=metadata)) 47 | 48 | return documents 49 | -------------------------------------------------------------------------------- /dapr_agents/document/reader/text.py: -------------------------------------------------------------------------------- 1 | from dapr_agents.document.reader.base import ReaderBase 2 | from dapr_agents.types.document import Document 3 | from pathlib import Path 4 | from typing import List 5 | from pydantic import Field 6 | 7 | 8 | class TextLoader(ReaderBase): 9 | """ 10 | Loader for plain text files. 11 | 12 | Attributes: 13 | encoding (str): The text file encoding. Defaults to 'utf-8'. 14 | """ 15 | 16 | encoding: str = Field(default="utf-8", description="Encoding of the text file.") 17 | 18 | def load(self, file_path: Path) -> List[Document]: 19 | """ 20 | Load content from a plain text file. 21 | 22 | Args: 23 | file_path (Path): Path to the text file. 24 | 25 | Returns: 26 | List[Document]: A list containing one Document object. 27 | """ 28 | if not file_path.is_file(): 29 | raise FileNotFoundError(f"File not found: {file_path}") 30 | 31 | content = file_path.read_text(encoding=self.encoding).strip() 32 | metadata = { 33 | "file_path": str(file_path), 34 | "file_type": "text", 35 | } 36 | return [Document(text=content, metadata=metadata)] 37 | -------------------------------------------------------------------------------- /dapr_agents/document/splitter/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import SplitterBase 2 | from .text import TextSplitter 3 | -------------------------------------------------------------------------------- /dapr_agents/executors/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import CodeExecutorBase 2 | from .local import LocalCodeExecutor 3 | from .docker import DockerCodeExecutor 4 | -------------------------------------------------------------------------------- /dapr_agents/executors/base.py: -------------------------------------------------------------------------------- 1 | from dapr_agents.types.executor import ExecutionRequest, CodeSnippet, ExecutionResult 2 | from abc import ABC, abstractmethod 3 | from pydantic import BaseModel 4 | from typing import List, ClassVar 5 | 6 | 7 | class CodeExecutorBase(BaseModel, ABC): 8 | """Abstract base class for executing code in different environments.""" 9 | 10 | SUPPORTED_LANGUAGES: ClassVar[set] = {"python", "sh", "bash"} 11 | 12 | @abstractmethod 13 | async def execute(self, request: ExecutionRequest) -> List[ExecutionResult]: 14 | """Executes the provided code snippets and returns results.""" 15 | pass 16 | 17 | def validate_snippets(self, snippets: List[CodeSnippet]) -> bool: 18 | """Ensures all code snippets are valid before execution.""" 19 | for snippet in snippets: 20 | if snippet.language not in self.SUPPORTED_LANGUAGES: 21 | raise ValueError(f"Unsupported language: {snippet.language}") 22 | return True 23 | -------------------------------------------------------------------------------- /dapr_agents/llm/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import LLMClientBase 2 | from .chat import ChatClientBase 3 | from .openai.client import OpenAIClient, AzureOpenAIClient 4 | from .openai.chat import OpenAIChatClient 5 | from .openai.audio import OpenAIAudioClient 6 | from .openai.embeddings import OpenAIEmbeddingClient 7 | from .huggingface.client import HFHubInferenceClientBase 8 | from .huggingface.chat import HFHubChatClient 9 | from .nvidia.client import NVIDIAClientBase 10 | from .nvidia.chat import NVIDIAChatClient 11 | from .nvidia.embeddings import NVIDIAEmbeddingClient 12 | from .elevenlabs import ElevenLabsSpeechClient 13 | from .dapr import DaprChatClient 14 | -------------------------------------------------------------------------------- /dapr_agents/llm/base.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, PrivateAttr 2 | from abc import ABC, abstractmethod 3 | from typing import Any 4 | 5 | 6 | class LLMClientBase(BaseModel, ABC): 7 | """ 8 | Abstract base class for LLM models. 9 | """ 10 | 11 | # Private attributes for provider and api 12 | _provider: str = PrivateAttr() 13 | _api: str = PrivateAttr() 14 | 15 | # Private attributes for config and client 16 | _config: Any = PrivateAttr() 17 | _client: Any = PrivateAttr() 18 | 19 | @property 20 | def provider(self) -> str: 21 | return self._provider 22 | 23 | @property 24 | def api(self) -> str: 25 | return self._api 26 | 27 | @property 28 | def config(self) -> Any: 29 | return self._config 30 | 31 | @property 32 | def client(self) -> Any: 33 | return self._client 34 | 35 | @abstractmethod 36 | def get_client(self) -> Any: 37 | """Abstract method to get the client for the LLM model.""" 38 | pass 39 | 40 | @abstractmethod 41 | def get_config(self) -> Any: 42 | """Abstract method to get the configuration for the LLM model.""" 43 | pass 44 | 45 | def refresh_client(self) -> None: 46 | """ 47 | Public method to refresh the client by regenerating the config and client. 48 | """ 49 | # Refresh config and client using the current state 50 | self._config = self.get_config() 51 | self._client = self.get_client() 52 | -------------------------------------------------------------------------------- /dapr_agents/llm/dapr/__init__.py: -------------------------------------------------------------------------------- 1 | from .chat import DaprChatClient 2 | from .client import DaprInferenceClientBase 3 | -------------------------------------------------------------------------------- /dapr_agents/llm/elevenlabs/__init__.py: -------------------------------------------------------------------------------- 1 | from .speech import ElevenLabsSpeechClient 2 | -------------------------------------------------------------------------------- /dapr_agents/llm/huggingface/__init__.py: -------------------------------------------------------------------------------- 1 | from .chat import HFHubChatClient 2 | from .client import HFHubInferenceClientBase 3 | -------------------------------------------------------------------------------- /dapr_agents/llm/nvidia/__init__.py: -------------------------------------------------------------------------------- 1 | from .client import NVIDIAClientBase 2 | from .chat import NVIDIAChatClient 3 | from .embeddings import NVIDIAEmbeddingClient 4 | -------------------------------------------------------------------------------- /dapr_agents/llm/openai/__init__.py: -------------------------------------------------------------------------------- 1 | from .client import OpenAIClient, AzureOpenAIClient 2 | from .chat import OpenAIChatClient 3 | from .audio import OpenAIAudioClient 4 | from .embeddings import OpenAIEmbeddingClient 5 | -------------------------------------------------------------------------------- /dapr_agents/llm/openai/client/__init__.py: -------------------------------------------------------------------------------- 1 | from .openai import OpenAIClient 2 | from .azure import AzureOpenAIClient 3 | from .base import OpenAIClientBase 4 | -------------------------------------------------------------------------------- /dapr_agents/llm/utils/__init__.py: -------------------------------------------------------------------------------- 1 | from .structure import StructureHandler 2 | from .stream import StreamHandler 3 | from .request import RequestHandler 4 | from .response import ResponseHandler 5 | from .http import HTTPHelper 6 | -------------------------------------------------------------------------------- /dapr_agents/llm/utils/http.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | import httpx 3 | 4 | 5 | class HTTPHelper: 6 | """ 7 | HTTP operations helper. 8 | """ 9 | 10 | @staticmethod 11 | def configure_timeout(timeout: Union[int, float, dict]) -> httpx.Timeout: 12 | """ 13 | Configure the timeout setting for the HTTP client. 14 | :param timeout: Timeout in seconds or a dictionary of timeout configurations. 15 | :return: An httpx.Timeout instance configured with the provided timeout. 16 | """ 17 | if isinstance(timeout, (int, float)): 18 | return httpx.Timeout(timeout) 19 | elif isinstance(timeout, dict): 20 | return httpx.Timeout(**timeout) 21 | else: 22 | return httpx.Timeout(30) 23 | -------------------------------------------------------------------------------- /dapr_agents/memory/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import MemoryBase 2 | from .liststore import ConversationListMemory 3 | from .vectorstore import ConversationVectorMemory 4 | from .daprstatestore import ConversationDaprStateMemory 5 | -------------------------------------------------------------------------------- /dapr_agents/prompt/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import PromptTemplateBase 2 | from .chat import ChatPromptTemplate 3 | from .string import StringPromptTemplate 4 | from .prompty import Prompty 5 | from .utils.prompty import PromptyHelper 6 | -------------------------------------------------------------------------------- /dapr_agents/prompt/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/dapr_agents/prompt/utils/__init__.py -------------------------------------------------------------------------------- /dapr_agents/prompt/utils/fstring.py: -------------------------------------------------------------------------------- 1 | from typing import Any, List 2 | 3 | 4 | def render_fstring_template(template: str, **kwargs: Any) -> str: 5 | """ 6 | Render an f-string style template by formatting it with the provided variables. 7 | 8 | Args: 9 | template (str): The f-string style template. 10 | **kwargs: Variables to be used for formatting the template. 11 | 12 | Returns: 13 | str: The rendered template string with variables replaced. 14 | """ 15 | return template.format(**kwargs) 16 | 17 | 18 | def extract_fstring_variables(template: str) -> List[str]: 19 | """ 20 | Extract variables from an f-string style template. 21 | 22 | Args: 23 | template (str): The f-string style template. 24 | 25 | Returns: 26 | List[str]: A list of variable names found in the template. 27 | """ 28 | return [ 29 | var.strip("{}") 30 | for var in template.split() 31 | if var.startswith("{") and var.endswith("}") 32 | ] 33 | -------------------------------------------------------------------------------- /dapr_agents/prompt/utils/jinja.py: -------------------------------------------------------------------------------- 1 | from jinja2 import Environment, Template 2 | from jinja2.meta import find_undeclared_variables 3 | from typing import List, Any 4 | 5 | 6 | def render_jinja_template(template: str, **kwargs: Any) -> str: 7 | """ 8 | Render a Jinja2 template using the provided variables. 9 | 10 | Args: 11 | template (str): The Jinja2 template string. 12 | **kwargs: Variables to be used in rendering the template. 13 | 14 | Returns: 15 | str: The rendered template string. 16 | """ 17 | return Template(template).render(**kwargs) 18 | 19 | 20 | def extract_jinja_variables(template: str) -> List[str]: 21 | """ 22 | Extract undeclared variables from a Jinja2 template. These variables represent placeholders 23 | that need to be filled in during rendering. 24 | 25 | Args: 26 | template (str): The Jinja2 template string. 27 | 28 | Returns: 29 | List[str]: A list of undeclared variable names in the template. 30 | """ 31 | environment = Environment() 32 | parsed_content = environment.parse(template) 33 | 34 | # Extract all undeclared variables (placeholders) 35 | undeclared_variables = find_undeclared_variables(parsed_content) 36 | 37 | return list(undeclared_variables) 38 | -------------------------------------------------------------------------------- /dapr_agents/service/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import APIServerBase 2 | from .fastapi import DaprFastAPIServer 3 | -------------------------------------------------------------------------------- /dapr_agents/service/base.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | from pydantic import BaseModel, Field 3 | from typing import Optional 4 | 5 | 6 | class APIServerBase(BaseModel, ABC): 7 | """ 8 | Abstract base class for API server services. 9 | Supports both FastAPI and Flask implementations. 10 | """ 11 | 12 | service_name: str = Field(..., description="The name of the API service.") 13 | service_port: Optional[int] = Field( 14 | default=None, 15 | description="Port to run the API server on. If None, use a random available port.", 16 | ) 17 | service_host: str = Field("0.0.0.0", description="Host address for the API server.") 18 | 19 | @abstractmethod 20 | async def start(self, log_level=None): 21 | """ 22 | Abstract method to start the API server. 23 | Must be implemented by subclasses. 24 | """ 25 | pass 26 | 27 | @abstractmethod 28 | async def stop(self): 29 | """ 30 | Abstract method to stop the API server. 31 | Must be implemented by subclasses. 32 | """ 33 | pass 34 | -------------------------------------------------------------------------------- /dapr_agents/service/fastapi/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import FastAPIServerBase 2 | from .dapr import DaprFastAPIServer 3 | -------------------------------------------------------------------------------- /dapr_agents/service/fastapi/dapr.py: -------------------------------------------------------------------------------- 1 | from dapr_agents.service.fastapi.base import FastAPIServerBase 2 | from dapr.ext.fastapi import DaprApp 3 | from pydantic import Field 4 | from typing import Optional, Any 5 | import logging 6 | 7 | logger = logging.getLogger(__name__) 8 | 9 | 10 | class DaprFastAPIServer(FastAPIServerBase): 11 | """ 12 | A Dapr-enabled service class extending FastAPIServerBase with Dapr-specific functionality. 13 | """ 14 | 15 | # Initialized in model_post_init 16 | dapr_app: Optional[DaprApp] = Field( 17 | default=None, init=False, description="DaprApp for pub/sub integration." 18 | ) 19 | 20 | def model_post_init(self, __context: Any) -> None: 21 | """ 22 | Post-initialization to configure the FastAPI app and Dapr-specific settings. 23 | """ 24 | # Initialize inherited FastAPI app setup 25 | super().model_post_init(__context) 26 | 27 | # Initialize DaprApp for pub/sub 28 | self.dapr_app = DaprApp(self.app) 29 | 30 | logger.info(f"{self.service_name} DaprFastAPIServer initialized.") 31 | -------------------------------------------------------------------------------- /dapr_agents/storage/__init__.py: -------------------------------------------------------------------------------- 1 | from .graphstores import GraphStoreBase, Neo4jGraphStore 2 | from .vectorstores import VectorStoreBase, ChromaVectorStore, PostgresVectorStore 3 | -------------------------------------------------------------------------------- /dapr_agents/storage/daprstores/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import DaprStoreBase 2 | from .statestore import DaprStateStore 3 | -------------------------------------------------------------------------------- /dapr_agents/storage/daprstores/base.py: -------------------------------------------------------------------------------- 1 | from dapr.clients import DaprClient 2 | from pydantic import BaseModel, Field, ConfigDict 3 | from typing import Optional, Any 4 | 5 | 6 | class DaprStoreBase(BaseModel): 7 | """ 8 | Pydantic-based Dapr store base model with configuration options for store name, address, host, and port. 9 | """ 10 | 11 | store_name: str = Field(..., description="The name of the Dapr store.") 12 | client: Optional[DaprClient] = Field( 13 | default=None, init=False, description="Dapr client for store operations." 14 | ) 15 | 16 | model_config = ConfigDict(arbitrary_types_allowed=True) 17 | 18 | def model_post_init(self, __context: Any) -> None: 19 | """ 20 | Post-initialization to set Dapr settings based on provided or environment values for host and port. 21 | """ 22 | 23 | # Complete post-initialization 24 | super().model_post_init(__context) 25 | -------------------------------------------------------------------------------- /dapr_agents/storage/daprstores/secretstore.py: -------------------------------------------------------------------------------- 1 | from dapr_agents.storage.daprstores.base import DaprStoreBase 2 | from typing import Dict, Optional 3 | 4 | 5 | class DaprSecretStore(DaprStoreBase): 6 | def get_secret( 7 | self, key: str, secret_metadata: Optional[Dict[str, str]] = {} 8 | ) -> Optional[Dict[str, str]]: 9 | """ 10 | Retrieves a secret from the secret store using the provided key. 11 | 12 | Args: 13 | key (str): The key for the secret. 14 | secret_metadata (Dict[str, str], optional): Metadata for the secret request. 15 | 16 | Returns: 17 | Optional[Dict[str, str]]: The secret stored in the secret store, or None if not found. 18 | """ 19 | response = self.client.get_secret( 20 | store_name=self.store_name, key=key, secret_metadata=secret_metadata 21 | ) 22 | return response.secret 23 | 24 | def get_bulk_secret( 25 | self, secret_metadata: Optional[Dict[str, str]] = {} 26 | ) -> Dict[str, Dict[str, str]]: 27 | """ 28 | Retrieves all granted secrets from the secret store. 29 | 30 | Args: 31 | secret_metadata (Dict[str, str], optional): Metadata for the secret request. 32 | 33 | Returns: 34 | Dict[str, Dict[str, str]]: A dictionary of secrets. 35 | """ 36 | response = self.client.get_bulk_secret( 37 | store_name=self.store_name, secret_metadata=secret_metadata 38 | ) 39 | return response.secrets 40 | -------------------------------------------------------------------------------- /dapr_agents/storage/graphstores/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import GraphStoreBase 2 | from .neo4j import Neo4jGraphStore 3 | -------------------------------------------------------------------------------- /dapr_agents/storage/graphstores/neo4j/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import Neo4jGraphStore 2 | from .client import Neo4jClient 3 | -------------------------------------------------------------------------------- /dapr_agents/storage/vectorstores/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import VectorStoreBase 2 | from .chroma import ChromaVectorStore 3 | from .postgres import PostgresVectorStore 4 | -------------------------------------------------------------------------------- /dapr_agents/tool/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import AgentTool, tool 2 | from .executor import AgentToolExecutor 3 | -------------------------------------------------------------------------------- /dapr_agents/tool/http/__init__.py: -------------------------------------------------------------------------------- 1 | from .client import DaprHTTPClient 2 | -------------------------------------------------------------------------------- /dapr_agents/tool/mcp/__init__.py: -------------------------------------------------------------------------------- 1 | from .client import MCPClient, create_sync_mcp_client 2 | from .transport import connect_stdio, connect_sse 3 | from .schema import create_pydantic_model_from_schema 4 | from .prompt import convert_prompt_message 5 | -------------------------------------------------------------------------------- /dapr_agents/tool/storage/__init__.py: -------------------------------------------------------------------------------- 1 | from .vectorstore import VectorToolStore 2 | -------------------------------------------------------------------------------- /dapr_agents/tool/utils/__init__.py: -------------------------------------------------------------------------------- 1 | from .openapi import OpenAPISpecParser 2 | from .tool import ToolHelper 3 | -------------------------------------------------------------------------------- /dapr_agents/types/__init__.py: -------------------------------------------------------------------------------- 1 | from .tools import OAIFunctionDefinition, OAIToolDefinition, ClaudeToolDefinition 2 | from .message import ( 3 | BaseMessage, 4 | MessageContent, 5 | ChatCompletion, 6 | SystemMessage, 7 | UserMessage, 8 | AssistantMessage, 9 | AssistantFinalMessage, 10 | ToolMessage, 11 | ToolCall, 12 | FunctionCall, 13 | MessagePlaceHolder, 14 | EventMessageMetadata, 15 | ) 16 | from .llm import OpenAIChatCompletionParams, OpenAIModelConfig 17 | from .exceptions import ( 18 | ToolError, 19 | AgentError, 20 | AgentToolExecutorError, 21 | StructureError, 22 | FunCallBuilderError, 23 | ) 24 | from .graph import Node, Relationship 25 | from .schemas import OAIJSONSchema, OAIResponseFormatSchema 26 | -------------------------------------------------------------------------------- /dapr_agents/types/document.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, Field 2 | from typing import Optional, Dict, Any 3 | 4 | 5 | class Document(BaseModel): 6 | """ 7 | Represents a document with text content and associated metadata. 8 | """ 9 | 10 | metadata: Optional[Dict[str, Any]] = Field( 11 | default=None, 12 | description="A dictionary containing metadata about the document (e.g., source, page number).", 13 | ) 14 | text: str = Field(..., description="The main content of the document.") 15 | -------------------------------------------------------------------------------- /dapr_agents/types/exceptions.py: -------------------------------------------------------------------------------- 1 | class AgentToolExecutorError(Exception): 2 | """Custom exception for AgentToolExecutor specific errors.""" 3 | 4 | 5 | class AgentError(Exception): 6 | """Custom exception for Agent specific errors, used to handle errors specific to agent operations.""" 7 | 8 | 9 | class ToolError(Exception): 10 | """Custom exception for tool-related errors.""" 11 | 12 | 13 | class StructureError(Exception): 14 | """Custom exception for errors related to structured handling.""" 15 | 16 | 17 | class FunCallBuilderError(Exception): 18 | """Custom exception for errors related to structured handling.""" 19 | -------------------------------------------------------------------------------- /dapr_agents/types/executor.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | from pydantic import BaseModel, Field 3 | 4 | 5 | class ExecutionResult(BaseModel): 6 | """Stores the outcome of a code execution.""" 7 | 8 | status: str = Field( 9 | ..., description="The execution status, either 'success' or 'error'." 10 | ) 11 | output: str = Field( 12 | ..., 13 | description="The standard output or error message resulting from execution.", 14 | ) 15 | exit_code: int = Field( 16 | ..., 17 | description="The exit code returned by the executed process (0 indicates success, non-zero indicates an error).", 18 | ) 19 | 20 | 21 | class CodeSnippet(BaseModel): 22 | """Represents a block of code extracted for execution.""" 23 | 24 | language: str = Field( 25 | ..., 26 | description="The programming language of the code snippet (e.g., 'python', 'javascript').", 27 | ) 28 | code: str = Field(..., description="The actual source code to be executed.") 29 | timeout: int = Field( 30 | 5, 31 | description="Per-snippet timeout (seconds). Executor falls back to the request-level timeout if omitted.", 32 | ) 33 | 34 | 35 | class ExecutionRequest(BaseModel): 36 | """Represents a request to execute a code snippet.""" 37 | 38 | snippets: List[CodeSnippet] = Field( 39 | ..., 40 | description="A list of code snippets to be executed sequentially or in parallel.", 41 | ) 42 | timeout: int = Field( 43 | 5, 44 | description="The maximum time (in seconds) allowed for execution before timing out (default is 5 seconds).", 45 | ) 46 | -------------------------------------------------------------------------------- /dapr_agents/types/schemas.py: -------------------------------------------------------------------------------- 1 | from typing import Any, Dict, Optional 2 | from typing_extensions import Literal 3 | from pydantic import BaseModel, Field 4 | 5 | 6 | class OAIJSONSchema(BaseModel): 7 | """ 8 | Defines the content for a JSON schema used in OpenAI's response_format. 9 | """ 10 | 11 | name: str = Field( 12 | ..., 13 | description="The name of the response format.", 14 | json_schema_extra={ 15 | "maxLength": 64, 16 | "pattern": "^[a-zA-Z0-9_-]+$", 17 | "examples": ["example_schema_name"], 18 | }, 19 | ) 20 | description: Optional[str] = Field( 21 | None, 22 | description="Explains the purpose of this response format so the model knows how to respond.", 23 | ) 24 | schema_: Optional[Dict[str, Any]] = Field( 25 | None, 26 | serialization_alias="schema", 27 | description="The underlying JSON Schema object describing the response format structure.", 28 | ) 29 | strict: bool = Field( 30 | True, 31 | description=( 32 | "Whether to enforce strict schema adherence when generating the output. " 33 | "If set to True, only a subset of JSON Schema features is supported." 34 | ), 35 | ) 36 | 37 | 38 | class OAIResponseFormatSchema(BaseModel): 39 | """ 40 | Represents the top-level structure for OpenAI's 'json_schema' response format. 41 | """ 42 | 43 | type: Literal["json_schema"] = Field( 44 | "json_schema", 45 | description="Specifies that this response format is a JSON schema definition.", 46 | ) 47 | json_schema: OAIJSONSchema = Field( 48 | ..., 49 | description="Contains metadata and the actual JSON schema for the structured output.", 50 | ) 51 | -------------------------------------------------------------------------------- /dapr_agents/types/workflow.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class DaprWorkflowStatus(str, Enum): 5 | """Enumeration of possible workflow statuses for standardized tracking.""" 6 | 7 | UNKNOWN = "unknown" # Workflow is in an undefined state 8 | RUNNING = "running" # Workflow is actively running 9 | COMPLETED = "completed" # Workflow has completed 10 | FAILED = "failed" # Workflow encountered an error 11 | TERMINATED = "terminated" # Workflow was canceled or forcefully terminated 12 | SUSPENDED = "suspended" # Workflow was temporarily paused 13 | PENDING = "pending" # Workflow is waiting to start 14 | -------------------------------------------------------------------------------- /dapr_agents/workflow/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import WorkflowApp 2 | from .task import WorkflowTask 3 | from .agentic import AgenticWorkflow 4 | from .orchestrators import LLMOrchestrator, RandomOrchestrator, RoundRobinOrchestrator 5 | from .agents import AssistantAgent 6 | from .decorators import workflow, task 7 | -------------------------------------------------------------------------------- /dapr_agents/workflow/agents/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import AgentWorkflowBase 2 | from .assistant import AssistantAgent 3 | -------------------------------------------------------------------------------- /dapr_agents/workflow/agents/assistant/__init__.py: -------------------------------------------------------------------------------- 1 | from .agent import AssistantAgent 2 | -------------------------------------------------------------------------------- /dapr_agents/workflow/agents/assistant/schemas.py: -------------------------------------------------------------------------------- 1 | from dapr_agents.types.message import BaseMessage 2 | from pydantic import BaseModel, Field 3 | from typing import Optional 4 | 5 | 6 | class BroadcastMessage(BaseMessage): 7 | """ 8 | Represents a broadcast message from an agent. 9 | """ 10 | 11 | 12 | class AgentTaskResponse(BaseMessage): 13 | """ 14 | Represents a response message from an agent after completing a task. 15 | """ 16 | 17 | workflow_instance_id: Optional[str] = Field( 18 | default=None, description="Dapr workflow instance id from source if available" 19 | ) 20 | 21 | 22 | class TriggerAction(BaseModel): 23 | """ 24 | Represents a message used to trigger an agent's activity within the workflow. 25 | """ 26 | 27 | task: Optional[str] = Field( 28 | None, 29 | description="The specific task to execute. If not provided, the agent will act based on its memory or predefined behavior.", 30 | ) 31 | iteration: Optional[int] = Field(0, description="") 32 | workflow_instance_id: Optional[str] = Field( 33 | default=None, description="Dapr workflow instance id from source if available" 34 | ) 35 | -------------------------------------------------------------------------------- /dapr_agents/workflow/messaging/__init__.py: -------------------------------------------------------------------------------- 1 | from .routing import MessageRoutingMixin 2 | from .decorator import message_router 3 | from .pubsub import DaprPubSub 4 | -------------------------------------------------------------------------------- /dapr_agents/workflow/messaging/utils.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel 2 | from dataclasses import is_dataclass 3 | from typing import Any, Union, get_args, get_origin 4 | 5 | 6 | def is_pydantic_model(cls: Any) -> bool: 7 | return isinstance(cls, type) and issubclass(cls, BaseModel) 8 | 9 | 10 | def is_valid_routable_model(cls: Any) -> bool: 11 | return is_dataclass(cls) or is_pydantic_model(cls) 12 | 13 | 14 | def is_supported_model(cls: Any) -> bool: 15 | """Checks if a class is a supported message schema (Pydantic, dataclass, or dict).""" 16 | return cls is dict or is_dataclass(cls) or is_pydantic_model(cls) 17 | 18 | 19 | def extract_message_models(type_hint: Any) -> list[type]: 20 | """ 21 | Extracts one or more message types from a type hint. 22 | 23 | Supports: 24 | - Single type hint: `MyMessage` 25 | - Union types: `Union[MessageA, MessageB]` 26 | - Fallback to empty list if not valid 27 | """ 28 | if type_hint is None: 29 | return [] 30 | 31 | origin = get_origin(type_hint) 32 | if origin is Union: 33 | return list(get_args(type_hint)) 34 | else: 35 | return [type_hint] 36 | -------------------------------------------------------------------------------- /dapr_agents/workflow/orchestrators/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import OrchestratorWorkflowBase 2 | from .llm import LLMOrchestrator 3 | from .random import RandomOrchestrator 4 | from .roundrobin import RoundRobinOrchestrator 5 | -------------------------------------------------------------------------------- /dapr_agents/workflow/orchestrators/llm/__init__.py: -------------------------------------------------------------------------------- 1 | from .orchestrator import LLMOrchestrator 2 | -------------------------------------------------------------------------------- /dapr_agents/workflow/utils.py: -------------------------------------------------------------------------------- 1 | import inspect 2 | import logging 3 | from typing import Any, Callable, Dict 4 | 5 | logger = logging.getLogger(__name__) 6 | 7 | 8 | def get_decorated_methods(instance: Any, attribute_name: str) -> Dict[str, Callable]: 9 | """ 10 | Find all **public** bound methods on `instance` that carry a given decorator attribute. 11 | 12 | This will: 13 | 1. Inspect the class for functions or methods. 14 | 2. Bind them to the instance (so `self` is applied). 15 | 3. Filter in only those where `hasattr(method, attribute_name) is True`. 16 | 17 | Args: 18 | instance: Any object whose methods you want to inspect. 19 | attribute_name: 20 | The name of the attribute set by your decorator 21 | (e.g. "_is_task" or "_is_workflow"). 22 | 23 | Returns: 24 | A dict mapping `method_name` → `bound method`. 25 | 26 | Example: 27 | >>> class A: 28 | ... @task 29 | ... def foo(self): ... 30 | ... 31 | >>> get_decorated_methods(A(), "_is_task") 32 | {"foo": >} 33 | """ 34 | discovered: Dict[str, Callable] = {} 35 | 36 | cls = type(instance) 37 | for name, member in inspect.getmembers(cls, predicate=inspect.isfunction): 38 | # skip private/protected 39 | if name.startswith("_"): 40 | continue 41 | 42 | # bind to instance so that signature(self, ...) works 43 | try: 44 | bound = getattr(instance, name) 45 | except Exception as e: 46 | logger.warning(f"Could not bind method '{name}': {e}") 47 | continue 48 | 49 | # pick up only those with our decorator flag 50 | if hasattr(bound, attribute_name): 51 | discovered[name] = bound 52 | logger.debug(f"Discovered decorated method: {name}") 53 | 54 | return discovered 55 | -------------------------------------------------------------------------------- /dev-requirements.txt: -------------------------------------------------------------------------------- 1 | pydantic==2.10.5 2 | openai==1.59.6 3 | openapi-pydantic==0.5.1 4 | openapi-schema-pydantic==1.2.4 5 | regex>=2023.12.25 6 | Jinja2>=3.1.6 7 | azure-identity==1.19.0 8 | dapr>=1.15.0 9 | dapr-ext-fastapi==1.15.0 10 | dapr-ext-workflow==1.15.0 11 | colorama==0.4.6 12 | cloudevents==1.11.0 13 | pyyaml==6.0.2 14 | rich==13.9.4 15 | huggingface_hub==0.27.1 16 | numpy==2.2.2 17 | mypy==1.15.0 18 | mcp==1.7.1 19 | opentelemetry-distro==0.53b1 20 | opentelemetry-exporter-otlp==1.32.1 21 | opentelemetry-instrumentation-requests==0.53b1 -------------------------------------------------------------------------------- /docs/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM squidfunk/mkdocs-material 2 | 3 | RUN apk update && \ 4 | apk add gcc python3-dev musl-dev linux-headers && \ 5 | pip install mkdocs-jupyter -------------------------------------------------------------------------------- /docs/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/favicon.png -------------------------------------------------------------------------------- /docs/img/concepts-agents-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/concepts-agents-overview.png -------------------------------------------------------------------------------- /docs/img/concepts-agents.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/concepts-agents.png -------------------------------------------------------------------------------- /docs/img/concepts_agents_react_flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/concepts_agents_react_flow.png -------------------------------------------------------------------------------- /docs/img/concepts_agents_toolcall_flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/concepts_agents_toolcall_flow.png -------------------------------------------------------------------------------- /docs/img/home_concepts_principles_decoupled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/home_concepts_principles_decoupled.png -------------------------------------------------------------------------------- /docs/img/home_concepts_principles_message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/home_concepts_principles_message.png -------------------------------------------------------------------------------- /docs/img/home_concepts_principles_modular.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/home_concepts_principles_modular.png -------------------------------------------------------------------------------- /docs/img/home_concepts_principles_workflows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/home_concepts_principles_workflows.png -------------------------------------------------------------------------------- /docs/img/home_installation_init.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/home_installation_init.png -------------------------------------------------------------------------------- /docs/img/home_installation_redis_dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/home_installation_redis_dashboard.png -------------------------------------------------------------------------------- /docs/img/logo-workflows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/logo-workflows.png -------------------------------------------------------------------------------- /docs/img/workflows_dapr_activity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/workflows_dapr_activity.png -------------------------------------------------------------------------------- /docs/img/workflows_dapr_llm_request.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/workflows_dapr_llm_request.png -------------------------------------------------------------------------------- /docs/img/workflows_llm_agent_initialization_hobbit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/workflows_llm_agent_initialization_hobbit.png -------------------------------------------------------------------------------- /docs/img/workflows_llm_agent_initialization_wizard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/workflows_llm_agent_initialization_wizard.png -------------------------------------------------------------------------------- /docs/img/workflows_llm_console_logs_activities.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/workflows_llm_console_logs_activities.png -------------------------------------------------------------------------------- /docs/img/workflows_llm_console_logs_activities_chat_completions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/workflows_llm_console_logs_activities_chat_completions.png -------------------------------------------------------------------------------- /docs/img/workflows_llm_console_logs_complete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/workflows_llm_console_logs_complete.png -------------------------------------------------------------------------------- /docs/img/workflows_llm_orchestrator_initialization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/workflows_llm_orchestrator_initialization.png -------------------------------------------------------------------------------- /docs/img/workflows_llm_redis_agents_metadata.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/workflows_llm_redis_agents_metadata.png -------------------------------------------------------------------------------- /docs/img/workflows_llm_redis_broadcast_channel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/workflows_llm_redis_broadcast_channel.png -------------------------------------------------------------------------------- /docs/img/workflows_llm_redis_empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/workflows_llm_redis_empty.png -------------------------------------------------------------------------------- /docs/img/workflows_llm_redis_reset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/workflows_llm_redis_reset.png -------------------------------------------------------------------------------- /docs/img/workflows_llm_redis_workflow_state.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/workflows_llm_redis_workflow_state.png -------------------------------------------------------------------------------- /docs/img/workflows_llm_redis_workflow_state_edit_mode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/workflows_llm_redis_workflow_state_edit_mode.png -------------------------------------------------------------------------------- /docs/img/workflows_llm_workflow_state_json_object.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/workflows_llm_workflow_state_json_object.png -------------------------------------------------------------------------------- /docs/img/workflows_llm_workflow_state_json_object_plan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/workflows_llm_workflow_state_json_object_plan.png -------------------------------------------------------------------------------- /docs/img/workflows_llm_zipkin_portal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/workflows_llm_zipkin_portal.png -------------------------------------------------------------------------------- /docs/img/workflows_llm_zipkin_spans_finish.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/workflows_llm_zipkin_spans_finish.png -------------------------------------------------------------------------------- /docs/img/workflows_llm_zipkin_spans_start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/workflows_llm_zipkin_spans_start.png -------------------------------------------------------------------------------- /docs/img/workflows_original_activity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/workflows_original_activity.png -------------------------------------------------------------------------------- /docs/img/workflows_originial_llm_request.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/workflows_originial_llm_request.png -------------------------------------------------------------------------------- /docs/img/workflows_random_event_driven.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/workflows_random_event_driven.png -------------------------------------------------------------------------------- /docs/img/workflows_random_zipkin_portal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/workflows_random_zipkin_portal.png -------------------------------------------------------------------------------- /docs/img/workflows_random_zipkin_spans.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/workflows_random_zipkin_spans.png -------------------------------------------------------------------------------- /docs/img/workflows_roundrobin_agent_initialization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/workflows_roundrobin_agent_initialization.png -------------------------------------------------------------------------------- /docs/img/workflows_roundrobin_agents_health.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/workflows_roundrobin_agents_health.png -------------------------------------------------------------------------------- /docs/img/workflows_roundrobin_console_logs_activities.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/workflows_roundrobin_console_logs_activities.png -------------------------------------------------------------------------------- /docs/img/workflows_roundrobin_console_logs_complete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/workflows_roundrobin_console_logs_complete.png -------------------------------------------------------------------------------- /docs/img/workflows_roundrobin_redis_agents_metadata.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/workflows_roundrobin_redis_agents_metadata.png -------------------------------------------------------------------------------- /docs/img/workflows_roundrobin_redis_broadcast_channel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/workflows_roundrobin_redis_broadcast_channel.png -------------------------------------------------------------------------------- /docs/img/workflows_roundrobin_redis_empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/workflows_roundrobin_redis_empty.png -------------------------------------------------------------------------------- /docs/img/workflows_roundrobin_redis_reset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/workflows_roundrobin_redis_reset.png -------------------------------------------------------------------------------- /docs/img/workflows_roundrobin_zipkin_portal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/workflows_roundrobin_zipkin_portal.png -------------------------------------------------------------------------------- /docs/img/workflows_roundrobin_zipkin_spans.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/img/workflows_roundrobin_zipkin_spans.png -------------------------------------------------------------------------------- /docs/logo-sticker-text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/logo-sticker-text.png -------------------------------------------------------------------------------- /docs/logo-sticker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/logo-sticker.png -------------------------------------------------------------------------------- /docs/logo-workflows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/docs/logo-workflows.png -------------------------------------------------------------------------------- /mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | python_version = 3.10 3 | warn_unused_configs = True 4 | warn_redundant_casts = True 5 | show_error_codes = True 6 | check_untyped_defs = True 7 | install_types = True 8 | non_interactive = True 9 | 10 | files = dapr_agents/**/*.py 11 | 12 | exclude = 13 | cookbook/**/*.py, 14 | quickstarts/**/*.py, 15 | 16 | [mypy-dapr_agents.agent.*] 17 | ignore_errors = True 18 | 19 | [mypy-dapr_agents.agent.telemetry.*] 20 | ignore_errors = False 21 | 22 | [mypy-dapr_agents.document.*] 23 | ignore_errors = True 24 | 25 | [mypy-dapr_agents.executors.*] 26 | ignore_errors = True 27 | 28 | [mypy-dapr_agents.llm.*] 29 | ignore_errors = True 30 | 31 | [mypy-dapr_agents.memory.*] 32 | ignore_errors = True 33 | 34 | [mypy-dapr_agents.prompt.*] 35 | ignore_errors = True 36 | 37 | [mypy-dapr_agents.service.*] 38 | ignore_errors = True 39 | 40 | [mypy-dapr_agents.storage.*] 41 | ignore_errors = True 42 | 43 | [mypy-dapr_agents.tool.mcp.*] 44 | ignore_errors = True 45 | 46 | [mypy-dapr_agents.tool.utils.*] 47 | ignore_errors = True 48 | 49 | [mypy-dapr_agents.tool.openapi.*] 50 | ignore_errors = True 51 | 52 | [mypy-dapr_agents.tool.base.*] 53 | ignore_errors = True 54 | 55 | [mypy-dapr_agents.tool.http.*] 56 | ignore_errors = False 57 | 58 | [mypy-dapr_agents.types.*] 59 | ignore_errors = True 60 | 61 | [mypy-dapr_agents.workflow.*] 62 | ignore_errors = True -------------------------------------------------------------------------------- /quickstarts/01-hello-world/01_ask_llm.py: -------------------------------------------------------------------------------- 1 | from dapr_agents import OpenAIChatClient 2 | from dotenv import load_dotenv 3 | 4 | load_dotenv() 5 | llm = OpenAIChatClient() 6 | response = llm.generate("Tell me a joke") 7 | if len(response.get_content()) > 0: 8 | print("Got response:", response.get_content()) 9 | -------------------------------------------------------------------------------- /quickstarts/01-hello-world/02_build_agent.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from dapr_agents import tool, Agent 3 | from dotenv import load_dotenv 4 | 5 | load_dotenv() 6 | 7 | 8 | @tool 9 | def my_weather_func() -> str: 10 | """Get current weather.""" 11 | return "It's 72°F and sunny" 12 | 13 | 14 | async def main(): 15 | weather_agent = Agent( 16 | name="WeatherAgent", 17 | role="Weather Assistant", 18 | instructions=["Help users with weather information"], 19 | tools=[my_weather_func], 20 | ) 21 | 22 | response = await weather_agent.run("What's the weather?") 23 | print(response) 24 | 25 | 26 | if __name__ == "__main__": 27 | asyncio.run(main()) 28 | -------------------------------------------------------------------------------- /quickstarts/01-hello-world/03_reason_act.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from dapr_agents import tool, ReActAgent 3 | from dotenv import load_dotenv 4 | 5 | load_dotenv() 6 | 7 | 8 | @tool 9 | def search_weather(city: str) -> str: 10 | """Get weather information for a city.""" 11 | weather_data = {"london": "rainy", "paris": "sunny"} 12 | return weather_data.get(city.lower(), "Unknown") 13 | 14 | 15 | @tool 16 | def get_activities(weather: str) -> str: 17 | """Get activity recommendations.""" 18 | activities = {"rainy": "Visit museums", "sunny": "Go hiking"} 19 | return activities.get(weather.lower(), "Stay comfortable") 20 | 21 | 22 | async def main(): 23 | react_agent = ReActAgent( 24 | name="TravelAgent", 25 | role="Travel Assistant", 26 | instructions=["Check weather, then suggest activities"], 27 | tools=[search_weather, get_activities], 28 | ) 29 | 30 | result = await react_agent.run("What should I do in London today?") 31 | if result: 32 | print("Result:", result) 33 | 34 | 35 | if __name__ == "__main__": 36 | asyncio.run(main()) 37 | -------------------------------------------------------------------------------- /quickstarts/01-hello-world/05_chain_tasks.py: -------------------------------------------------------------------------------- 1 | from dapr_agents.workflow import WorkflowApp, workflow, task 2 | from dapr.ext.workflow import DaprWorkflowContext 3 | 4 | from dotenv import load_dotenv 5 | 6 | load_dotenv() 7 | 8 | 9 | @workflow(name="analyze_topic") 10 | def analyze_topic(ctx: DaprWorkflowContext, topic: str): 11 | # Each step is durable and can be retried 12 | outline = yield ctx.call_activity(create_outline, input=topic) 13 | if len(outline) > 0: 14 | print("Outline:", outline) 15 | blog_post = yield ctx.call_activity(write_blog, input=outline) 16 | if len(blog_post) > 0: 17 | print("Blog post:", blog_post) 18 | return blog_post 19 | 20 | 21 | @task(description="Create a short outline about {topic}") 22 | def create_outline(topic: str) -> str: 23 | pass 24 | 25 | 26 | @task(description="Write a short blog post following this outline: {outline}") 27 | def write_blog(outline: str) -> str: 28 | pass 29 | 30 | 31 | if __name__ == "__main__": 32 | wfapp = WorkflowApp() 33 | 34 | results = wfapp.run_and_monitor_workflow_sync(analyze_topic, input="AI Agents") 35 | if len(results) > 0: 36 | print(f"Result: {results}") 37 | -------------------------------------------------------------------------------- /quickstarts/01-hello-world/components/conversationmemory.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: conversationstore 5 | spec: 6 | type: state.redis 7 | version: v1 8 | metadata: 9 | - name: redisHost 10 | value: localhost:6379 11 | - name: redisPassword 12 | value: "" -------------------------------------------------------------------------------- /quickstarts/01-hello-world/components/pubsub.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: messagepubsub 5 | spec: 6 | type: pubsub.redis 7 | version: v1 8 | metadata: 9 | - name: redisHost 10 | value: localhost:6379 11 | - name: redisPassword 12 | value: "" 13 | - name: consumerID 14 | value: "travel-planner-group" 15 | - name: enableTLS 16 | value: "false" -------------------------------------------------------------------------------- /quickstarts/01-hello-world/components/statestore.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: registrystatestore 5 | spec: 6 | type: state.redis 7 | version: v1 8 | metadata: 9 | - name: redisHost 10 | value: localhost:6379 11 | - name: redisPassword 12 | value: "" 13 | - name: enableTLS 14 | value: "false" -------------------------------------------------------------------------------- /quickstarts/01-hello-world/components/workflowstate.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: workflowstatestore 5 | spec: 6 | type: state.redis 7 | version: v1 8 | metadata: 9 | - name: redisHost 10 | value: localhost:6379 11 | - name: redisPassword 12 | value: "" 13 | - name: actorStateStore 14 | value: "true" -------------------------------------------------------------------------------- /quickstarts/01-hello-world/requirements.txt: -------------------------------------------------------------------------------- 1 | dapr-agents>=0.5.0 2 | python-dotenv -------------------------------------------------------------------------------- /quickstarts/02_llm_call_dapr/basic.prompty: -------------------------------------------------------------------------------- 1 | --- 2 | name: Basic Prompt 3 | description: A basic prompt that uses the chat API to answer questions 4 | model: 5 | api: chat 6 | configuration: 7 | type: nvidia 8 | name: meta/llama3-8b-instruct 9 | parameters: 10 | max_tokens: 128 11 | temperature: 0.2 12 | inputs: 13 | question: 14 | type: string 15 | sample: 16 | "question": "Who is the most famous person in the world?" 17 | --- 18 | system: 19 | You are an AI assistant who helps people find information. 20 | As the assistant, you answer questions briefly, succinctly. 21 | 22 | user: 23 | {{question}} -------------------------------------------------------------------------------- /quickstarts/02_llm_call_dapr/components/awsbedrock.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: awsbedrock 5 | spec: 6 | type: conversation.aws.bedrock 7 | metadata: 8 | - name: endpoint 9 | value: "http://localhost:4566" 10 | - name: model 11 | value: amazon.titan-text-express-v1 12 | - name: cacheTTL 13 | value: 10m 14 | -------------------------------------------------------------------------------- /quickstarts/02_llm_call_dapr/components/echo.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: echo 5 | spec: 6 | type: conversation.echo 7 | version: v1 -------------------------------------------------------------------------------- /quickstarts/02_llm_call_dapr/components/openai.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: openai 5 | spec: 6 | type: conversation.openai 7 | metadata: 8 | - name: key 9 | value: 10 | - name: model 11 | value: gpt-4-turbo 12 | - name: cacheTTL 13 | value: 10m -------------------------------------------------------------------------------- /quickstarts/02_llm_call_dapr/components/resiliency.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Resiliency 3 | metadata: 4 | name: awsbedrock-resiliency 5 | spec: 6 | policies: 7 | timeouts: 8 | short-timeout: 1s 9 | retries: 10 | fixed-retry: 11 | policy: constant 12 | duration: 1s 13 | maxRetries: 3 14 | targets: 15 | components: 16 | awsbedrock: 17 | outbound: 18 | timeout: short-timeout 19 | retry: fixed-retry 20 | -------------------------------------------------------------------------------- /quickstarts/02_llm_call_dapr/requirements.txt: -------------------------------------------------------------------------------- 1 | dapr-agents>=0.5.0 2 | python-dotenv 3 | -------------------------------------------------------------------------------- /quickstarts/02_llm_call_dapr/text_completion.py: -------------------------------------------------------------------------------- 1 | from dapr_agents.llm import DaprChatClient 2 | from dapr_agents.types import UserMessage 3 | from dotenv import load_dotenv 4 | 5 | # Load environment variables from .env 6 | load_dotenv() 7 | 8 | # Basic chat completion 9 | llm = DaprChatClient() 10 | response = llm.generate("Name a famous dog!") 11 | 12 | if len(response.get_content()) > 0: 13 | print("Response: ", response.get_content()) 14 | 15 | # Chat completion using a prompty file for context 16 | llm = DaprChatClient.from_prompty("basic.prompty") 17 | response = llm.generate(input_data={"question": "What is your name?"}) 18 | 19 | if len(response.get_content()) > 0: 20 | print("Response with prompty: ", response.get_content()) 21 | 22 | # Chat completion with user input 23 | llm = DaprChatClient() 24 | response = llm.generate(messages=[UserMessage("hello")]) 25 | 26 | 27 | if len(response.get_content()) > 0 and "hello" in response.get_content().lower(): 28 | print("Response with user input: ", response.get_content()) 29 | -------------------------------------------------------------------------------- /quickstarts/02_llm_call_elevenlabs/requirements.txt: -------------------------------------------------------------------------------- 1 | dapr-agents>=0.5.0 2 | python-dotenv 3 | elevenlabs -------------------------------------------------------------------------------- /quickstarts/02_llm_call_elevenlabs/text_to_speech.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from dapr_agents import ElevenLabsSpeechClient 4 | from dotenv import load_dotenv 5 | 6 | load_dotenv() 7 | 8 | client = ElevenLabsSpeechClient( 9 | model="eleven_multilingual_v2", # Default model 10 | voice="JBFqnCBsd6RMkjVDRZzb", # 'name': 'George', 'language': 'en', 'labels': {'accent': 'British', 'description': 'warm', 'age': 'middle aged', 'gender': 'male', 'use_case': 'narration'} 11 | ) 12 | 13 | 14 | # Define the text to convert to speech 15 | text = "Dapr Agents is an open-source framework for researchers and developers" 16 | 17 | # Create speech from text 18 | audio_bytes = client.create_speech( 19 | text=text, 20 | output_format="mp3_44100_128", # default output format, mp3 with 44.1kHz sample rate at 128kbps. 21 | ) 22 | 23 | # You can also automatically create the audio file by passing the file name as an argument 24 | # client.create_speech( 25 | # text=text, 26 | # output_format="mp3_44100_128", # default output format, mp3 with 44.1kHz sample rate at 128kbps., 27 | # file_name='output_speech_auto.mp3' 28 | # ) 29 | 30 | 31 | # Save the audio to an MP3 file 32 | output_path = "output_speech.mp3" 33 | with open(output_path, "wb") as audio_file: 34 | audio_file.write(audio_bytes) 35 | 36 | print(f"Audio saved to {output_path}") 37 | 38 | os.remove(output_path) 39 | print(f"File {output_path} has been deleted.") 40 | -------------------------------------------------------------------------------- /quickstarts/02_llm_call_hugging_face/basic.prompty: -------------------------------------------------------------------------------- 1 | --- 2 | name: Basic Prompt 3 | description: A basic prompt that uses the chat API to answer questions 4 | model: 5 | api: chat 6 | configuration: 7 | type: huggingface 8 | name: microsoft/Phi-3-mini-4k-instruct 9 | parameters: 10 | max_tokens: 128 11 | temperature: 0.2 12 | inputs: 13 | question: 14 | type: string 15 | sample: 16 | "question": "Who is the most famous person in the world?" 17 | --- 18 | system: 19 | You are an AI assistant who helps people find information. 20 | As the assistant, you answer questions briefly, succinctly. 21 | 22 | user: 23 | {{question}} -------------------------------------------------------------------------------- /quickstarts/02_llm_call_hugging_face/requirements.txt: -------------------------------------------------------------------------------- 1 | dapr-agents>=0.5.0 -------------------------------------------------------------------------------- /quickstarts/02_llm_call_hugging_face/text_completion.py: -------------------------------------------------------------------------------- 1 | from dapr_agents.llm import HFHubChatClient 2 | from dapr_agents.types import UserMessage 3 | 4 | from dotenv import load_dotenv 5 | 6 | load_dotenv() 7 | 8 | # Basic chat completion 9 | llm = HFHubChatClient(model="microsoft/Phi-3-mini-4k-instruct") 10 | response = llm.generate("Name a famous dog!") 11 | 12 | if len(response.get_content()) > 0: 13 | print("Response: ", response.get_content()) 14 | 15 | # Chat completion using a prompty file for context 16 | llm = HFHubChatClient.from_prompty("basic.prompty") 17 | response = llm.generate(input_data={"question": "What is your name?"}) 18 | 19 | if len(response.get_content()) > 0: 20 | print("Response with prompty: ", response.get_content()) 21 | 22 | # Chat completion with user input 23 | llm = HFHubChatClient(model="microsoft/Phi-3-mini-4k-instruct") 24 | response = llm.generate(messages=[UserMessage("hello")]) 25 | 26 | 27 | if len(response.get_content()) > 0 and "hello" in response.get_content().lower(): 28 | print("Response with user input: ", response.get_content()) 29 | -------------------------------------------------------------------------------- /quickstarts/02_llm_call_nvidia/basic.prompty: -------------------------------------------------------------------------------- 1 | --- 2 | name: Basic Prompt 3 | description: A basic prompt that uses the chat API to answer questions 4 | model: 5 | api: chat 6 | configuration: 7 | type: nvidia 8 | name: meta/llama3-8b-instruct 9 | parameters: 10 | max_tokens: 128 11 | temperature: 0.2 12 | inputs: 13 | question: 14 | type: string 15 | sample: 16 | "question": "Who is the most famous person in the world?" 17 | --- 18 | system: 19 | You are an AI assistant who helps people find information. 20 | As the assistant, you answer questions briefly, succinctly. 21 | 22 | user: 23 | {{question}} -------------------------------------------------------------------------------- /quickstarts/02_llm_call_nvidia/embeddings.py: -------------------------------------------------------------------------------- 1 | from dapr_agents.document.embedder import NVIDIAEmbedder 2 | from dotenv import load_dotenv 3 | 4 | load_dotenv() 5 | 6 | # Initialize the embedder 7 | embedder = NVIDIAEmbedder( 8 | model="nvidia/nv-embedqa-e5-v5", # Default embedding model 9 | ) 10 | 11 | # Generate embedding with a single text 12 | text = "Dapr Agents is an open-source framework for researchers and developers" 13 | 14 | embedding = embedder.embed(text) 15 | 16 | # Display the embedding 17 | if len(embedding) > 0: 18 | print(f"Embedding (first 5 values): {embedding[:5]}...") 19 | 20 | # Multiple input texts 21 | texts = [ 22 | "Dapr Agents is an open-source framework for researchers and developers", 23 | "It provides tools to create, orchestrate, and manage agents", 24 | ] 25 | 26 | # Generate embeddings 27 | embeddings = embedder.embed(texts) 28 | 29 | if len(embeddings) == 0: 30 | print("No embeddings generated") 31 | exit() 32 | 33 | # Display the embeddings 34 | for i, emb in enumerate(embeddings): 35 | print(f"Text {i + 1} embedding (first 5 values): {emb[:5]}") 36 | -------------------------------------------------------------------------------- /quickstarts/02_llm_call_nvidia/requirements.txt: -------------------------------------------------------------------------------- 1 | dapr-agents>=0.5.0 2 | python-dotenv 3 | tiktoken -------------------------------------------------------------------------------- /quickstarts/02_llm_call_nvidia/structured_completion.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from dapr_agents import NVIDIAChatClient 4 | from dapr_agents.types import UserMessage 5 | from pydantic import BaseModel 6 | from dotenv import load_dotenv 7 | 8 | # Load environment variables from .env 9 | load_dotenv() 10 | 11 | 12 | # Define our data model 13 | class Dog(BaseModel): 14 | name: str 15 | breed: str 16 | reason: str 17 | 18 | 19 | # Initialize the chat client 20 | llm = NVIDIAChatClient(model="meta/llama-3.1-8b-instruct") 21 | 22 | # Get structured response 23 | response = llm.generate( 24 | messages=[UserMessage("One famous dog in history.")], response_format=Dog 25 | ) 26 | 27 | print(json.dumps(response.model_dump(), indent=2)) 28 | -------------------------------------------------------------------------------- /quickstarts/02_llm_call_nvidia/text_completion.py: -------------------------------------------------------------------------------- 1 | from dapr_agents import NVIDIAChatClient 2 | from dapr_agents.types import UserMessage 3 | from dotenv import load_dotenv 4 | 5 | # Load environment variables from .env 6 | load_dotenv() 7 | 8 | # Basic chat completion 9 | llm = NVIDIAChatClient() 10 | response = llm.generate("Name a famous dog!") 11 | 12 | if len(response.get_content()) > 0: 13 | print("Response: ", response.get_content()) 14 | 15 | # Chat completion using a prompty file for context 16 | llm = NVIDIAChatClient.from_prompty("basic.prompty") 17 | response = llm.generate(input_data={"question": "What is your name?"}) 18 | 19 | if len(response.get_content()) > 0: 20 | print("Response with prompty: ", response.get_content()) 21 | 22 | # Chat completion with user input 23 | llm = NVIDIAChatClient() 24 | response = llm.generate(messages=[UserMessage("hello")]) 25 | 26 | 27 | if len(response.get_content()) > 0 and "hello" in response.get_content().lower(): 28 | print("Response with user input: ", response.get_content()) 29 | -------------------------------------------------------------------------------- /quickstarts/02_llm_call_open_ai/audio_transcription.py: -------------------------------------------------------------------------------- 1 | from dapr_agents.types.llm import AudioTranscriptionRequest 2 | from dapr_agents import OpenAIAudioClient 3 | from dotenv import load_dotenv 4 | 5 | load_dotenv() 6 | client = OpenAIAudioClient() 7 | 8 | # Specify the audio file to transcribe 9 | audio_file_path = "speech.mp3" 10 | 11 | # Create a transcription request 12 | transcription_request = AudioTranscriptionRequest( 13 | model="whisper-1", file=audio_file_path 14 | ) 15 | 16 | ############ 17 | # You can also use audio bytes: 18 | ############ 19 | # 20 | # with open(audio_file_path, "rb") as f: 21 | # audio_bytes = f.read() 22 | # 23 | # transcription_request = AudioTranscriptionRequest( 24 | # model="whisper-1", 25 | # file=audio_bytes, # File as bytes 26 | # language="en" # Optional: Specify the language of the audio 27 | # ) 28 | 29 | 30 | # Generate transcription 31 | transcription_response = client.create_transcription(request=transcription_request) 32 | 33 | # Display the transcription result 34 | if not len(transcription_response.text) > 0: 35 | exit(1) 36 | 37 | print("Transcription:", transcription_response.text) 38 | 39 | words = ["dapr", "agents", "open", "source", "framework", "researchers", "developers"] 40 | normalized_text = transcription_response.text.lower() 41 | 42 | count = 0 43 | for word in words: 44 | if word in normalized_text: 45 | count += 1 46 | 47 | if count >= 5: 48 | print("Success! The transcription contains at least 5 out of 7 words.") 49 | -------------------------------------------------------------------------------- /quickstarts/02_llm_call_open_ai/audio_translation.py: -------------------------------------------------------------------------------- 1 | from dapr_agents.types.llm import AudioTranslationRequest 2 | from dapr_agents import OpenAIAudioClient 3 | from dotenv import load_dotenv 4 | 5 | load_dotenv() 6 | client = OpenAIAudioClient() 7 | 8 | # Specify the audio file to translate 9 | audio_file_path = "speech_spanish.mp3" 10 | 11 | # Create a translation request 12 | translation_request = AudioTranslationRequest( 13 | model="whisper-1", 14 | file=audio_file_path, 15 | prompt="The user will provide an audio file in Spanish. Translate the audio to English and transcribe the english text, word for word.", 16 | ) 17 | 18 | # Generate translation 19 | translation_response = client.create_translation(request=translation_request) 20 | 21 | # Display the transcription result 22 | if not len(translation_response.text) > 0: 23 | exit(1) 24 | 25 | print("Translation:", translation_response) 26 | 27 | words = ["dapr", "agents", "open", "source", "framework", "researchers", "developers"] 28 | normalized_text = translation_response.text.lower() 29 | 30 | count = 0 31 | for word in words: 32 | if word in normalized_text: 33 | count += 1 34 | 35 | if count >= 5: 36 | print("Success! The transcription contains at least 5 out of 7 words.") 37 | -------------------------------------------------------------------------------- /quickstarts/02_llm_call_open_ai/basic.prompty: -------------------------------------------------------------------------------- 1 | --- 2 | name: Basic Prompt 3 | description: A basic prompt that uses the chat API to answer questions 4 | model: 5 | api: chat 6 | configuration: 7 | type: openai 8 | name: gpt-4o 9 | parameters: 10 | max_tokens: 128 11 | temperature: 0.2 12 | inputs: 13 | question: 14 | type: string 15 | sample: 16 | "question": "Who is the most famous person in the world?" 17 | --- 18 | system: 19 | You are an AI assistant who helps people find information. 20 | As the assistant, you answer questions briefly, succinctly. 21 | 22 | user: 23 | {{question}} -------------------------------------------------------------------------------- /quickstarts/02_llm_call_open_ai/embeddings.py: -------------------------------------------------------------------------------- 1 | from dapr_agents.document.embedder import OpenAIEmbedder 2 | from dotenv import load_dotenv 3 | 4 | load_dotenv() 5 | 6 | # Initialize the embedder 7 | embedder = OpenAIEmbedder( 8 | model="text-embedding-ada-002", # Default embedding model 9 | chunk_size=1000, # Batch size for processing 10 | max_tokens=8191, # Maximum tokens per input 11 | ) 12 | 13 | # Generate embedding with a single text 14 | text = "Dapr Agents is an open-source framework for researchers and developers" 15 | 16 | embedding = embedder.embed(text) 17 | 18 | # Display the embedding 19 | if len(embedding) > 0: 20 | print(f"Embedding (first 5 values): {embedding[:5]}...") 21 | 22 | # Multiple input texts 23 | texts = [ 24 | "Dapr Agents is an open-source framework for researchers and developers", 25 | "It provides tools to create, orchestrate, and manage agents", 26 | ] 27 | 28 | # Generate embeddings 29 | embeddings = embedder.embed(texts) 30 | 31 | if len(embeddings) == 0: 32 | print("No embeddings generated") 33 | exit() 34 | 35 | # Display the embeddings 36 | for i, emb in enumerate(embeddings): 37 | print(f"Text {i + 1} embedding (first 5 values): {emb[:5]}") 38 | -------------------------------------------------------------------------------- /quickstarts/02_llm_call_open_ai/requirements.txt: -------------------------------------------------------------------------------- 1 | dapr-agents>=0.5.0 2 | python-dotenv 3 | tiktoken -------------------------------------------------------------------------------- /quickstarts/02_llm_call_open_ai/speech.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/quickstarts/02_llm_call_open_ai/speech.mp3 -------------------------------------------------------------------------------- /quickstarts/02_llm_call_open_ai/speech_spanish.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/quickstarts/02_llm_call_open_ai/speech_spanish.mp3 -------------------------------------------------------------------------------- /quickstarts/02_llm_call_open_ai/structured_completion.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from dapr_agents import OpenAIChatClient 4 | from dapr_agents.types import UserMessage 5 | from pydantic import BaseModel 6 | from dotenv import load_dotenv 7 | 8 | # Load environment variables from .env 9 | load_dotenv() 10 | 11 | 12 | # Define our data model 13 | class Dog(BaseModel): 14 | name: str 15 | breed: str 16 | reason: str 17 | 18 | 19 | # Initialize the chat client 20 | llm = OpenAIChatClient() 21 | 22 | # Get structured response 23 | response = llm.generate( 24 | messages=[UserMessage("One famous dog in history.")], response_format=Dog 25 | ) 26 | 27 | print(json.dumps(response.model_dump(), indent=2)) 28 | -------------------------------------------------------------------------------- /quickstarts/02_llm_call_open_ai/text_completion.py: -------------------------------------------------------------------------------- 1 | from dapr_agents import OpenAIChatClient 2 | from dapr_agents.types import UserMessage 3 | from dotenv import load_dotenv 4 | 5 | # Load environment variables from .env 6 | load_dotenv() 7 | 8 | # Basic chat completion 9 | llm = OpenAIChatClient() 10 | response = llm.generate("Name a famous dog!") 11 | 12 | if len(response.get_content()) > 0: 13 | print("Response: ", response.get_content()) 14 | 15 | # Chat completion using a prompty file for context 16 | llm = OpenAIChatClient.from_prompty("basic.prompty") 17 | response = llm.generate(input_data={"question": "What is your name?"}) 18 | 19 | if len(response.get_content()) > 0: 20 | print("Response with prompty: ", response.get_content()) 21 | 22 | # Chat completion with user input 23 | llm = OpenAIChatClient() 24 | response = llm.generate(messages=[UserMessage("hello")]) 25 | 26 | 27 | if len(response.get_content()) > 0 and "hello" in response.get_content().lower(): 28 | print("Response with user input: ", response.get_content()) 29 | -------------------------------------------------------------------------------- /quickstarts/02_llm_call_open_ai/text_to_speech.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from dapr_agents.types.llm import AudioSpeechRequest 4 | from dapr_agents import OpenAIAudioClient 5 | from dotenv import load_dotenv 6 | 7 | load_dotenv() 8 | client = OpenAIAudioClient() 9 | 10 | # Define the text to convert to speech 11 | text_to_speech = ( 12 | "Dapr Agents is an open-source framework for researchers and developers" 13 | ) 14 | 15 | # Create a request for TTS 16 | tts_request = AudioSpeechRequest( 17 | model="tts-1", input=text_to_speech, voice="fable", response_format="mp3" 18 | ) 19 | 20 | # Generate the audio - returns a byte string 21 | audio_bytes = client.create_speech(request=tts_request) 22 | 23 | # You can also automatically create the audio file by passing the file name as an argument 24 | # client.create_speech(request=tts_request, file_name=output_path) 25 | 26 | # Save the audio to an MP3 file 27 | output_path = "output_speech.mp3" 28 | with open(output_path, "wb") as audio_file: 29 | audio_file.write(audio_bytes) 30 | 31 | print(f"Audio saved to {output_path}") 32 | 33 | os.remove(output_path) 34 | print(f"File {output_path} has been deleted.") 35 | -------------------------------------------------------------------------------- /quickstarts/03-agent-tool-call/components/historystore.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: historystore 5 | spec: 6 | type: state.redis 7 | version: v1 8 | metadata: 9 | - name: redisHost 10 | value: localhost:6379 11 | - name: redisPassword 12 | value: "" -------------------------------------------------------------------------------- /quickstarts/03-agent-tool-call/requirements.txt: -------------------------------------------------------------------------------- 1 | dapr-agents>=0.5.0 2 | python-dotenv -------------------------------------------------------------------------------- /quickstarts/03-agent-tool-call/weather_agent.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from weather_tools import tools 3 | from dapr_agents import Agent 4 | from dotenv import load_dotenv 5 | 6 | load_dotenv() 7 | 8 | AIAgent = Agent( 9 | name="Stevie", 10 | role="Weather Assistant", 11 | goal="Assist Humans with weather related tasks.", 12 | instructions=[ 13 | "Get accurate weather information", 14 | "From time to time, you can also jump after answering the weather question.", 15 | ], 16 | tools=tools, 17 | ) 18 | 19 | 20 | # Wrap your async call 21 | async def main(): 22 | await AIAgent.run("What is the weather in Virginia, New York and Washington DC?") 23 | 24 | 25 | if __name__ == "__main__": 26 | asyncio.run(main()) 27 | -------------------------------------------------------------------------------- /quickstarts/03-agent-tool-call/weather_agent_dapr.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from weather_tools import tools 3 | from dapr_agents import Agent 4 | from dotenv import load_dotenv 5 | from dapr_agents.memory import ConversationDaprStateMemory 6 | 7 | load_dotenv() 8 | 9 | AIAgent = Agent( 10 | name="Stevie", 11 | role="Weather Assistant", 12 | goal="Assist Humans with weather related tasks.", 13 | instructions=[ 14 | "Get accurate weather information", 15 | "From time to time, you can also jump after answering the weather question.", 16 | ], 17 | memory=ConversationDaprStateMemory(store_name="historystore", session_id="some-id"), 18 | pattern="toolcalling", 19 | tools=tools, 20 | ) 21 | 22 | 23 | # Wrap your async call 24 | async def main(): 25 | await AIAgent.run("What is the weather in Virginia, New York and Washington DC?") 26 | 27 | 28 | if __name__ == "__main__": 29 | asyncio.run(main()) 30 | -------------------------------------------------------------------------------- /quickstarts/03-agent-tool-call/weather_tools.py: -------------------------------------------------------------------------------- 1 | from dapr_agents import tool 2 | from pydantic import BaseModel, Field 3 | 4 | 5 | class GetWeatherSchema(BaseModel): 6 | location: str = Field(description="location to get weather for") 7 | 8 | 9 | @tool(args_model=GetWeatherSchema) 10 | def get_weather(location: str) -> str: 11 | """Get weather information based on location.""" 12 | import random 13 | 14 | temperature = random.randint(60, 80) 15 | return f"{location}: {temperature}F." 16 | 17 | 18 | class JumpSchema(BaseModel): 19 | distance: str = Field(description="Distance for agent to jump") 20 | 21 | 22 | @tool(args_model=JumpSchema) 23 | def jump(distance: str) -> str: 24 | """Jump a specific distance.""" 25 | return f"I jumped the following distance {distance}" 26 | 27 | 28 | tools = [get_weather, jump] 29 | -------------------------------------------------------------------------------- /quickstarts/04-agentic-workflow/components/workflowstate.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: workflowstatestore 5 | spec: 6 | type: state.redis 7 | version: v1 8 | metadata: 9 | - name: redisHost 10 | value: localhost:6379 11 | - name: redisPassword 12 | value: "" 13 | - name: actorStateStore 14 | value: "true" -------------------------------------------------------------------------------- /quickstarts/04-agentic-workflow/requirements.txt: -------------------------------------------------------------------------------- 1 | dapr-agents>=0.5.0 2 | python-dotenv -------------------------------------------------------------------------------- /quickstarts/04-agentic-workflow/sequential_workflow.py: -------------------------------------------------------------------------------- 1 | from dapr_agents.workflow import WorkflowApp, workflow, task 2 | from dapr.ext.workflow import DaprWorkflowContext 3 | from dotenv import load_dotenv 4 | 5 | # Load environment variables 6 | load_dotenv() 7 | 8 | 9 | # Define Workflow logic 10 | @workflow(name="task_chain_workflow") 11 | def task_chain_workflow(ctx: DaprWorkflowContext): 12 | result1 = yield ctx.call_activity(get_character) 13 | result2 = yield ctx.call_activity(get_line, input={"character": result1}) 14 | return result2 15 | 16 | 17 | @task( 18 | description=""" 19 | Pick a random character from The Lord of the Rings\n 20 | and respond with the character's name only 21 | """ 22 | ) 23 | def get_character() -> str: 24 | pass 25 | 26 | 27 | @task( 28 | description="What is a famous line by {character}", 29 | ) 30 | def get_line(character: str) -> str: 31 | pass 32 | 33 | 34 | if __name__ == "__main__": 35 | wfapp = WorkflowApp() 36 | 37 | results = wfapp.run_and_monitor_workflow_sync(task_chain_workflow) 38 | print(f"Famous Line: {results}") 39 | -------------------------------------------------------------------------------- /quickstarts/04-agentic-workflow/workflow_dapr_agent.py: -------------------------------------------------------------------------------- 1 | from dapr_agents.workflow import WorkflowApp, workflow, task 2 | from dapr.ext.workflow import DaprWorkflowContext 3 | from dotenv import load_dotenv 4 | 5 | # Load environment variables 6 | load_dotenv() 7 | 8 | 9 | # Define Workflow logic 10 | @workflow(name="task_chain_workflow") 11 | def task_chain_workflow(ctx: DaprWorkflowContext): 12 | character = yield ctx.call_activity(get_character) 13 | print(f"Character: {character}") 14 | line = yield ctx.call_activity(get_line, input={"character": character}) 15 | print(f"Line: {line}") 16 | return line 17 | 18 | 19 | @task( 20 | description=""" 21 | Pick a random character from The Lord of the Rings\n 22 | and respond with the character's name only 23 | """ 24 | ) 25 | def get_character() -> str: 26 | pass 27 | 28 | 29 | @task( 30 | description="What is a famous line by {character}", 31 | ) 32 | def get_line(character: str) -> str: 33 | pass 34 | 35 | 36 | if __name__ == "__main__": 37 | wfapp = WorkflowApp() 38 | 39 | results = wfapp.run_and_monitor_workflow_sync(task_chain_workflow) 40 | print(f"Results: {results}") 41 | -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-actors/components/agentstate.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: agentstatestore 5 | spec: 6 | type: state.redis 7 | version: v1 8 | metadata: 9 | - name: redisHost 10 | value: localhost:6379 11 | - name: redisPassword 12 | value: "" 13 | - name: keyPrefix 14 | value: none 15 | - name: actorStateStore 16 | value: "true" -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-actors/components/pubsub.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: messagepubsub 5 | spec: 6 | type: pubsub.redis 7 | version: v1 8 | metadata: 9 | - name: redisHost 10 | value: localhost:6379 11 | - name: redisPassword 12 | value: "" -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-actors/components/workflowstate.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: workflowstatestore 5 | spec: 6 | type: state.redis 7 | version: v1 8 | metadata: 9 | - name: redisHost 10 | value: localhost:6379 11 | - name: redisPassword 12 | value: "" -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-actors/dapr-llm.yaml: -------------------------------------------------------------------------------- 1 | # https://docs.dapr.io/developing-applications/local-development/multi-app-dapr-run/multi-app-template/#template-properties 2 | version: 1 3 | common: 4 | resourcesPath: ./components 5 | logLevel: info 6 | appLogDestination: console 7 | daprdLogDestination: console 8 | 9 | apps: 10 | - appID: HobbitApp 11 | appDirPath: ./services/hobbit/ 12 | command: ["python3", "app.py"] 13 | appPort: 8001 14 | 15 | - appID: WizardApp 16 | appDirPath: ./services/wizard/ 17 | command: ["python3", "app.py"] 18 | appPort: 8002 19 | 20 | - appID: ElfApp 21 | appDirPath: ./services/elf/ 22 | command: ["python3", "app.py"] 23 | appPort: 8003 24 | 25 | - appID: WorkflowApp 26 | appDirPath: ./services/workflow-llm/ 27 | command: ["python3", "app.py"] 28 | appPort: 8004 29 | 30 | - appID: ClientApp 31 | appDirPath: ./services/client/ 32 | command: ["python3", "http_client.py"] -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-actors/dapr-random.yaml: -------------------------------------------------------------------------------- 1 | # https://docs.dapr.io/developing-applications/local-development/multi-app-dapr-run/multi-app-template/#template-properties 2 | version: 1 3 | common: 4 | resourcesPath: ./components 5 | logLevel: info 6 | appLogDestination: console 7 | daprdLogDestination: console 8 | 9 | apps: 10 | - appID: HobbitApp 11 | appDirPath: ./services/hobbit/ 12 | appPort: 8001 13 | command: ["python3", "app.py"] 14 | 15 | - appID: WizardApp 16 | appDirPath: ./services/wizard/ 17 | appPort: 8002 18 | command: ["python3", "app.py"] 19 | 20 | - appID: ElfApp 21 | appDirPath: ./services/elf/ 22 | appPort: 8003 23 | command: ["python3", "app.py"] 24 | 25 | - appID: WorkflowApp 26 | appDirPath: ./services/workflow-random/ 27 | command: ["python3", "app.py"] 28 | appPort: 8004 29 | 30 | - appID: ClientApp 31 | appDirPath: ./services/client/ 32 | command: ["python3", "http_client.py"] -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-actors/dapr-roundrobin.yaml: -------------------------------------------------------------------------------- 1 | # https://docs.dapr.io/developing-applications/local-development/multi-app-dapr-run/multi-app-template/#template-properties 2 | version: 1 3 | common: 4 | resourcesPath: ./components 5 | logLevel: info 6 | appLogDestination: console 7 | daprdLogDestination: console 8 | 9 | apps: 10 | - appID: HobbitApp 11 | appDirPath: ./services/hobbit/ 12 | appPort: 8001 13 | command: ["python3", "app.py"] 14 | 15 | - appID: WizardApp 16 | appDirPath: ./services/wizard/ 17 | appPort: 8002 18 | command: ["python3", "app.py"] 19 | 20 | - appID: ElfApp 21 | appDirPath: ./services/elf/ 22 | appPort: 8003 23 | command: ["python3", "app.py"] 24 | 25 | - appID: WorkflowApp 26 | appDirPath: ./services/workflow-roundrobin/ 27 | command: ["python3", "app.py"] 28 | appPort: 8004 29 | 30 | - appID: ClientApp 31 | appDirPath: ./services/client/ 32 | command: ["python3", "http_client.py"] -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-actors/requirements.txt: -------------------------------------------------------------------------------- 1 | dapr-agents>=0.5.0 2 | python-dotenv 3 | requests -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-actors/services/client/http_client.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import requests 3 | import time 4 | import sys 5 | 6 | 7 | if __name__ == "__main__": 8 | workflow_url = "http://localhost:8004/start-workflow" 9 | task_payload = {"task": "How to get to Mordor? We all need to help!"} 10 | 11 | attempt = 1 12 | 13 | while attempt <= 10: 14 | try: 15 | print(f"Attempt {attempt}...") 16 | response = requests.post(workflow_url, json=task_payload, timeout=5) 17 | 18 | if response.status_code == 202: 19 | print("Workflow started successfully!") 20 | sys.exit(0) 21 | else: 22 | print(f"Received status code {response.status_code}: {response.text}") 23 | 24 | except requests.exceptions.RequestException as e: 25 | print(f"Request failed: {e}") 26 | 27 | attempt += 1 28 | print("Waiting 1s seconds before next attempt...") 29 | time.sleep(1) 30 | 31 | print("Maximum attempts (10) reached without success.") 32 | 33 | print("Failed to get successful response") 34 | sys.exit(1) 35 | -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-actors/services/elf/app.py: -------------------------------------------------------------------------------- 1 | from dapr_agents import Agent, AgentActor 2 | from dotenv import load_dotenv 3 | import asyncio 4 | import logging 5 | 6 | 7 | async def main(): 8 | try: 9 | # Define Agent 10 | elf_agent = Agent( 11 | role="Elf", 12 | name="Legolas", 13 | goal="Act as a scout, marksman, and protector, using keen senses and deadly accuracy to ensure the success of the journey.", 14 | instructions=[ 15 | "Speak like Legolas, with grace, wisdom, and keen observation.", 16 | "Be swift, silent, and precise, moving effortlessly across any terrain.", 17 | "Use superior vision and heightened senses to scout ahead and detect threats.", 18 | "Excel in ranged combat, delivering pinpoint arrow strikes from great distances.", 19 | "Respond concisely, accurately, and relevantly, ensuring clarity and strict alignment with the task.", 20 | ], 21 | ) 22 | 23 | # Expose Agent as an Actor over a Service 24 | elf_actor = AgentActor( 25 | agent=elf_agent, 26 | message_bus_name="messagepubsub", 27 | agents_registry_store_name="agentstatestore", 28 | agents_registry_key="agents_registry", 29 | service_port=8003, 30 | ) 31 | 32 | await elf_actor.start() 33 | except Exception as e: 34 | print(f"Error starting actor: {e}") 35 | 36 | 37 | if __name__ == "__main__": 38 | load_dotenv() 39 | 40 | logging.basicConfig(level=logging.INFO) 41 | 42 | asyncio.run(main()) 43 | -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-actors/services/hobbit/app.py: -------------------------------------------------------------------------------- 1 | from dapr_agents import Agent, AgentActor 2 | from dotenv import load_dotenv 3 | import asyncio 4 | import logging 5 | 6 | 7 | async def main(): 8 | try: 9 | # Define Agent 10 | hobbit_agent = Agent( 11 | role="Hobbit", 12 | name="Frodo", 13 | goal="Carry the One Ring to Mount Doom, resisting its corruptive power while navigating danger and uncertainty.", 14 | instructions=[ 15 | "Speak like Frodo, with humility, determination, and a growing sense of resolve.", 16 | "Endure hardships and temptations, staying true to the mission even when faced with doubt.", 17 | "Seek guidance and trust allies, but bear the ultimate burden alone when necessary.", 18 | "Move carefully through enemy-infested lands, avoiding unnecessary risks.", 19 | "Respond concisely, accurately, and relevantly, ensuring clarity and strict alignment with the task.", 20 | ], 21 | ) 22 | 23 | # Expose Agent as an Actor over a Service 24 | hobbit_actor = AgentActor( 25 | agent=hobbit_agent, 26 | message_bus_name="messagepubsub", 27 | agents_registry_store_name="agentstatestore", 28 | agents_registry_key="agents_registry", 29 | service_port=8001, 30 | ) 31 | 32 | await hobbit_actor.start() 33 | except Exception as e: 34 | print(f"Error starting actor: {e}") 35 | 36 | 37 | if __name__ == "__main__": 38 | load_dotenv() 39 | 40 | logging.basicConfig(level=logging.INFO) 41 | 42 | asyncio.run(main()) 43 | -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-actors/services/wizard/app.py: -------------------------------------------------------------------------------- 1 | from dapr_agents import Agent, AgentActor 2 | from dotenv import load_dotenv 3 | import asyncio 4 | import logging 5 | 6 | 7 | async def main(): 8 | try: 9 | # Define Agent 10 | wizard_agent = Agent( 11 | role="Wizard", 12 | name="Gandalf", 13 | goal="Guide the Fellowship with wisdom and strategy, using magic and insight to ensure the downfall of Sauron.", 14 | instructions=[ 15 | "Speak like Gandalf, with wisdom, patience, and a touch of mystery.", 16 | "Provide strategic counsel, always considering the long-term consequences of actions.", 17 | "Use magic sparingly, applying it when necessary to guide or protect.", 18 | "Encourage allies to find strength within themselves rather than relying solely on your power.", 19 | "Respond concisely, accurately, and relevantly, ensuring clarity and strict alignment with the task.", 20 | ], 21 | ) 22 | 23 | # Expose Agent as an Actor over a Service 24 | wizard_actor = AgentActor( 25 | agent=wizard_agent, 26 | message_bus_name="messagepubsub", 27 | agents_registry_store_name="agentstatestore", 28 | agents_registry_key="agents_registry", 29 | service_port=8002, 30 | ) 31 | 32 | await wizard_actor.start() 33 | except Exception as e: 34 | print(f"Error starting actor: {e}") 35 | 36 | 37 | if __name__ == "__main__": 38 | load_dotenv() 39 | 40 | logging.basicConfig(level=logging.INFO) 41 | 42 | asyncio.run(main()) 43 | -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-actors/services/workflow-llm/app.py: -------------------------------------------------------------------------------- 1 | from dapr_agents import LLMOrchestrator 2 | from dotenv import load_dotenv 3 | import asyncio 4 | import logging 5 | 6 | 7 | async def main(): 8 | try: 9 | llm_workflow = LLMOrchestrator( 10 | name="LLMOrchestrator", 11 | message_bus_name="messagepubsub", 12 | state_store_name="workflowstatestore", 13 | state_key="workflow_state", 14 | agents_registry_store_name="agentstatestore", 15 | agents_registry_key="agents_registry", 16 | max_iterations=3, 17 | ).as_service(port=8004) 18 | 19 | await llm_workflow.start() 20 | except Exception as e: 21 | print(f"Error starting workflow: {e}") 22 | 23 | 24 | if __name__ == "__main__": 25 | load_dotenv() 26 | 27 | logging.basicConfig(level=logging.INFO) 28 | 29 | asyncio.run(main()) 30 | -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-actors/services/workflow-random/app.py: -------------------------------------------------------------------------------- 1 | from dapr_agents import RandomOrchestrator 2 | from dotenv import load_dotenv 3 | import asyncio 4 | import logging 5 | 6 | 7 | async def main(): 8 | try: 9 | random_workflow = RandomOrchestrator( 10 | name="RandomOrchestrator", 11 | message_bus_name="messagepubsub", 12 | state_store_name="workflowstatestore", 13 | state_key="workflow_state", 14 | agents_registry_store_name="agentstatestore", 15 | agents_registry_key="agents_registry", 16 | max_iterations=3, 17 | ).as_service(port=8004) 18 | 19 | await random_workflow.start() 20 | except Exception as e: 21 | print(f"Error starting workflow: {e}") 22 | 23 | 24 | if __name__ == "__main__": 25 | load_dotenv() 26 | 27 | logging.basicConfig(level=logging.INFO) 28 | 29 | asyncio.run(main()) 30 | -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-actors/services/workflow-roundrobin/app.py: -------------------------------------------------------------------------------- 1 | from dapr_agents import RoundRobinOrchestrator 2 | from dotenv import load_dotenv 3 | import asyncio 4 | import logging 5 | 6 | 7 | async def main(): 8 | try: 9 | roundrobin_workflow = RoundRobinOrchestrator( 10 | name="RoundRobinOrchestrator", 11 | message_bus_name="messagepubsub", 12 | state_store_name="workflowstatestore", 13 | state_key="workflow_state", 14 | agents_registry_store_name="agentstatestore", 15 | agents_registry_key="agents_registry", 16 | max_iterations=3, 17 | ).as_service(port=8004) 18 | 19 | await roundrobin_workflow.start() 20 | except Exception as e: 21 | print(f"Error starting workflow: {e}") 22 | 23 | 24 | if __name__ == "__main__": 25 | load_dotenv() 26 | 27 | logging.basicConfig(level=logging.INFO) 28 | 29 | asyncio.run(main()) 30 | -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-dapr-workflows/components/agentstate.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: agentstatestore 5 | spec: 6 | type: state.redis 7 | version: v1 8 | metadata: 9 | - name: redisHost 10 | value: localhost:6379 11 | - name: redisPassword 12 | value: "" 13 | - name: keyPrefix 14 | value: none 15 | - name: actorStateStore 16 | value: "true" -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-dapr-workflows/components/pubsub.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: messagepubsub 5 | spec: 6 | type: pubsub.redis 7 | version: v1 8 | metadata: 9 | - name: redisHost 10 | value: localhost:6379 11 | - name: redisPassword 12 | value: "" -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-dapr-workflows/components/workflowstate.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: workflowstatestore 5 | spec: 6 | type: state.redis 7 | version: v1 8 | metadata: 9 | - name: redisHost 10 | value: localhost:6379 11 | - name: redisPassword 12 | value: "" -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-dapr-workflows/dapr-llm.yaml: -------------------------------------------------------------------------------- 1 | # https://docs.dapr.io/developing-applications/local-development/multi-app-dapr-run/multi-app-template/#template-properties 2 | version: 1 3 | common: 4 | resourcesPath: ./components 5 | logLevel: info 6 | appLogDestination: console 7 | daprdLogDestination: console 8 | 9 | apps: 10 | - appID: HobbitApp 11 | appDirPath: ./services/hobbit/ 12 | command: ["python3", "app.py"] 13 | 14 | - appID: WizardApp 15 | appDirPath: ./services/wizard/ 16 | command: ["python3", "app.py"] 17 | 18 | - appID: ElfApp 19 | appDirPath: ./services/elf/ 20 | command: ["python3", "app.py"] 21 | 22 | - appID: WorkflowApp 23 | appDirPath: ./services/workflow-llm/ 24 | command: ["python3", "app.py"] 25 | appPort: 8004 26 | 27 | - appID: ClientApp 28 | appDirPath: ./services/client/ 29 | command: ["python3", "http_client.py"] -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-dapr-workflows/dapr-random.yaml: -------------------------------------------------------------------------------- 1 | # https://docs.dapr.io/developing-applications/local-development/multi-app-dapr-run/multi-app-template/#template-properties 2 | version: 1 3 | common: 4 | resourcesPath: ./components 5 | logLevel: info 6 | appLogDestination: console 7 | daprdLogDestination: console 8 | 9 | apps: 10 | - appID: HobbitApp 11 | appDirPath: ./services/hobbit/ 12 | command: ["python3", "app.py"] 13 | 14 | - appID: WizardApp 15 | appDirPath: ./services/wizard/ 16 | command: ["python3", "app.py"] 17 | 18 | - appID: ElfApp 19 | appDirPath: ./services/elf/ 20 | command: ["python3", "app.py"] 21 | 22 | - appID: WorkflowApp 23 | appDirPath: ./services/workflow-random/ 24 | command: ["python3", "app.py"] 25 | appPort: 8004 26 | 27 | - appID: ClientApp 28 | appDirPath: ./services/client/ 29 | command: ["python3", "http_client.py"] -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-dapr-workflows/dapr-roundrobin.yaml: -------------------------------------------------------------------------------- 1 | # https://docs.dapr.io/developing-applications/local-development/multi-app-dapr-run/multi-app-template/#template-properties 2 | version: 1 3 | common: 4 | resourcesPath: ./components 5 | logLevel: info 6 | appLogDestination: console 7 | daprdLogDestination: console 8 | 9 | apps: 10 | - appID: HobbitApp 11 | appDirPath: ./services/hobbit/ 12 | command: ["python3", "app.py"] 13 | 14 | - appID: WizardApp 15 | appDirPath: ./services/wizard/ 16 | command: ["python3", "app.py"] 17 | 18 | - appID: ElfApp 19 | appDirPath: ./services/elf/ 20 | command: ["python3", "app.py"] 21 | 22 | - appID: WorkflowApp 23 | appDirPath: ./services/workflow-roundrobin/ 24 | command: ["python3", "app.py"] 25 | appPort: 8004 26 | 27 | - appID: ClientApp 28 | appDirPath: ./services/client/ 29 | command: ["python3", "http_client.py"] -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-dapr-workflows/requirements.txt: -------------------------------------------------------------------------------- 1 | dapr-agents>=0.5.0 2 | python-dotenv 3 | requests -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-dapr-workflows/services/elf/app.py: -------------------------------------------------------------------------------- 1 | from dapr_agents import AssistantAgent 2 | from dotenv import load_dotenv 3 | import asyncio 4 | import logging 5 | 6 | 7 | async def main(): 8 | try: 9 | elf_service = AssistantAgent( 10 | name="Legolas", 11 | role="Elf", 12 | goal="Act as a scout, marksman, and protector, using keen senses and deadly accuracy to ensure the success of the journey.", 13 | instructions=[ 14 | "Speak like Legolas, with grace, wisdom, and keen observation.", 15 | "Be swift, silent, and precise, moving effortlessly across any terrain.", 16 | "Use superior vision and heightened senses to scout ahead and detect threats.", 17 | "Excel in ranged combat, delivering pinpoint arrow strikes from great distances.", 18 | "Respond concisely, accurately, and relevantly, ensuring clarity and strict alignment with the task.", 19 | ], 20 | message_bus_name="messagepubsub", 21 | state_store_name="workflowstatestore", 22 | state_key="workflow_state", 23 | agents_registry_store_name="agentstatestore", 24 | agents_registry_key="agents_registry", 25 | ) 26 | 27 | await elf_service.start() 28 | except Exception as e: 29 | print(f"Error starting service: {e}") 30 | 31 | 32 | if __name__ == "__main__": 33 | load_dotenv() 34 | 35 | logging.basicConfig(level=logging.INFO) 36 | 37 | asyncio.run(main()) 38 | -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-dapr-workflows/services/hobbit/app.py: -------------------------------------------------------------------------------- 1 | from dapr_agents import AssistantAgent 2 | from dotenv import load_dotenv 3 | import asyncio 4 | import logging 5 | 6 | 7 | async def main(): 8 | try: 9 | hobbit_service = AssistantAgent( 10 | name="Frodo", 11 | role="Hobbit", 12 | goal="Carry the One Ring to Mount Doom, resisting its corruptive power while navigating danger and uncertainty.", 13 | instructions=[ 14 | "Speak like Frodo, with humility, determination, and a growing sense of resolve.", 15 | "Endure hardships and temptations, staying true to the mission even when faced with doubt.", 16 | "Seek guidance and trust allies, but bear the ultimate burden alone when necessary.", 17 | "Move carefully through enemy-infested lands, avoiding unnecessary risks.", 18 | "Respond concisely, accurately, and relevantly, ensuring clarity and strict alignment with the task.", 19 | ], 20 | message_bus_name="messagepubsub", 21 | state_store_name="workflowstatestore", 22 | state_key="workflow_state", 23 | agents_registry_store_name="agentstatestore", 24 | agents_registry_key="agents_registry", 25 | ) 26 | 27 | await hobbit_service.start() 28 | except Exception as e: 29 | print(f"Error starting service: {e}") 30 | 31 | 32 | if __name__ == "__main__": 33 | load_dotenv() 34 | 35 | logging.basicConfig(level=logging.INFO) 36 | 37 | asyncio.run(main()) 38 | -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-dapr-workflows/services/wizard/app.py: -------------------------------------------------------------------------------- 1 | from dapr_agents import AssistantAgent 2 | from dotenv import load_dotenv 3 | import asyncio 4 | import logging 5 | 6 | 7 | async def main(): 8 | try: 9 | wizard_service = AssistantAgent( 10 | role="Wizard", 11 | name="Gandalf", 12 | goal="Guide the Fellowship with wisdom and strategy, using magic and insight to ensure the downfall of Sauron.", 13 | instructions=[ 14 | "Speak like Gandalf, with wisdom, patience, and a touch of mystery.", 15 | "Provide strategic counsel, always considering the long-term consequences of actions.", 16 | "Use magic sparingly, applying it when necessary to guide or protect.", 17 | "Encourage allies to find strength within themselves rather than relying solely on your power.", 18 | "Respond concisely, accurately, and relevantly, ensuring clarity and strict alignment with the task.", 19 | ], 20 | message_bus_name="messagepubsub", 21 | state_store_name="workflowstatestore", 22 | state_key="workflow_state", 23 | agents_registry_store_name="agentstatestore", 24 | agents_registry_key="agents_registry", 25 | ) 26 | 27 | await wizard_service.start() 28 | except Exception as e: 29 | print(f"Error starting service: {e}") 30 | 31 | 32 | if __name__ == "__main__": 33 | load_dotenv() 34 | 35 | logging.basicConfig(level=logging.INFO) 36 | 37 | asyncio.run(main()) 38 | -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-dapr-workflows/services/workflow-llm/app.py: -------------------------------------------------------------------------------- 1 | from dapr_agents import LLMOrchestrator 2 | from dotenv import load_dotenv 3 | import asyncio 4 | import logging 5 | 6 | 7 | async def main(): 8 | try: 9 | workflow_service = LLMOrchestrator( 10 | name="LLMOrchestrator", 11 | message_bus_name="messagepubsub", 12 | state_store_name="workflowstatestore", 13 | state_key="workflow_state", 14 | agents_registry_store_name="agentstatestore", 15 | agents_registry_key="agents_registry", 16 | max_iterations=3, 17 | ).as_service(port=8004) 18 | 19 | await workflow_service.start() 20 | except Exception as e: 21 | print(f"Error starting service: {e}") 22 | 23 | 24 | if __name__ == "__main__": 25 | load_dotenv() 26 | 27 | logging.basicConfig(level=logging.INFO) 28 | 29 | asyncio.run(main()) 30 | -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-dapr-workflows/services/workflow-random/app.py: -------------------------------------------------------------------------------- 1 | from dapr_agents import RandomOrchestrator 2 | from dotenv import load_dotenv 3 | import asyncio 4 | import logging 5 | 6 | 7 | async def main(): 8 | try: 9 | workflow_service = RandomOrchestrator( 10 | name="RandomOrchestrator", 11 | message_bus_name="messagepubsub", 12 | state_store_name="workflowstatestore", 13 | state_key="workflow_state", 14 | agents_registry_store_name="agentstatestore", 15 | agents_registry_key="agents_registry", 16 | max_iterations=3, 17 | ).as_service(port=8004) 18 | 19 | await workflow_service.start() 20 | except Exception as e: 21 | print(f"Error starting service: {e}") 22 | 23 | 24 | if __name__ == "__main__": 25 | load_dotenv() 26 | 27 | logging.basicConfig(level=logging.INFO) 28 | 29 | asyncio.run(main()) 30 | -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-dapr-workflows/services/workflow-roundrobin/app.py: -------------------------------------------------------------------------------- 1 | from dapr_agents import RoundRobinOrchestrator 2 | from dotenv import load_dotenv 3 | import asyncio 4 | import logging 5 | 6 | 7 | async def main(): 8 | try: 9 | workflow_service = RoundRobinOrchestrator( 10 | name="RoundRobinOrchestrator", 11 | message_bus_name="messagepubsub", 12 | state_store_name="workflowstatestore", 13 | state_key="workflow_state", 14 | agents_registry_store_name="agentstatestore", 15 | agents_registry_key="agents_registry", 16 | max_iterations=3, 17 | ).as_service(port=8004) 18 | 19 | await workflow_service.start() 20 | except Exception as e: 21 | print(f"Error starting service: {e}") 22 | 23 | 24 | if __name__ == "__main__": 25 | load_dotenv() 26 | 27 | logging.basicConfig(level=logging.INFO) 28 | 29 | asyncio.run(main()) 30 | -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-k8s/README.md: -------------------------------------------------------------------------------- 1 | # Run Multi agent workflows in Kubernetes 2 | 3 | This quickstart demonstrates how to create and orchestrate event-driven workflows with multiple autonomous agents using Dapr Agents running on Kubernetes. 4 | 5 | ## Prerequisites 6 | 7 | - Python 3.10 (recommended) 8 | - Pip package manager 9 | - OpenAI API key 10 | - Kind 11 | - Docker 12 | - Helm 13 | 14 | ## Configuration 15 | 16 | 1. Create a `.env` file for your API keys: 17 | 18 | ```env 19 | OPENAI_API_KEY=your_api_key_here 20 | ``` 21 | 22 | ## Install through script 23 | 24 | The script will: 25 | 26 | 1. Install Kind with a local registry 27 | 1. Install Bitnami Redis 28 | 1. Install Dapr 29 | 1. Build the images for [05-multi-agent-workflow-dapr-workflows](../05-multi-agent-workflow-dapr-workflows/) 30 | 1. Push the images to local in-cluster registry 31 | 1. Install the [components for the agents](./components/) 32 | 1. Create the kubernetes secret form `.env` file 33 | 1. Deploy the [manifests for the agents](./manifests/) 34 | 1. Port forward the `workload-llm` pod on port `8004` 35 | 1. Trigger the workflow for getting to Morder by [k8s_http_client.py](./services/client/k8s_http_client.py) 36 | 37 | ### Install through manifests 38 | 39 | First create a secret from your `.env` file: 40 | 41 | ```bash 42 | kubectl create secret generic openai-secrets --from-env-file=.env --namespace default --dry-run=client -o yaml | kubectl apply -f - 43 | ``` 44 | 45 | Then build the images locally with `docker-compose`: 46 | 47 | ```bash 48 | docker-compose -f docker-compose.yaml build --no-cache 49 | ``` 50 | 51 | Then deploy the manifests: 52 | 53 | ```bash 54 | kubectl apply -f manifests/ 55 | ``` 56 | 57 | Port forward the `workload-llm` pod: 58 | 59 | ```bash 60 | kubectl port-forward -n default svc/workflow-llm 8004:80 &>/dev/null & 61 | ``` 62 | 63 | Trigger the client: 64 | 65 | ```bash 66 | python3 services/client/k8s_http_client.py 67 | ``` 68 | -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-k8s/components/agentstate.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: agentstatestore 5 | spec: 6 | type: state.redis 7 | version: v1 8 | metadata: 9 | - name: redisHost 10 | value: dapr-redis-master:6379 11 | - name: redisPassword 12 | secretKeyRef: 13 | name: dapr-redis 14 | key: "redis-password" 15 | - name: keyPrefix 16 | value: none 17 | - name: actorStateStore 18 | value: "true" -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-k8s/components/pubsub.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: messagepubsub 5 | spec: 6 | type: pubsub.redis 7 | version: v1 8 | metadata: 9 | - name: redisHost 10 | value: dapr-redis-master:6379 11 | - name: redisPassword 12 | secretKeyRef: 13 | name: dapr-redis 14 | key: "redis-password" -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-k8s/components/workflowstate.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: workflowstatestore 5 | spec: 6 | type: state.redis 7 | version: v1 8 | metadata: 9 | - name: redisHost 10 | value: dapr-redis-master:6379 11 | - name: redisPassword 12 | secretKeyRef: 13 | name: dapr-redis 14 | key: "redis-password" -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-k8s/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | workflow-llm: 3 | image: localhost:5001/workflow-llm:latest 4 | build: 5 | context: ../ 6 | dockerfile: ./05-multi-agent-workflow-k8s/services/workflow-llm/Dockerfile 7 | elf: 8 | image: localhost:5001/elf:latest 9 | build: 10 | context: ../ 11 | dockerfile: ./05-multi-agent-workflow-k8s/services/elf/Dockerfile 12 | hobbit: 13 | image: localhost:5001/hobbit:latest 14 | build: 15 | context: ../ 16 | dockerfile: ./05-multi-agent-workflow-k8s/services/hobbit/Dockerfile 17 | wizard: 18 | image: localhost:5001/wizard:latest 19 | build: 20 | context: ../ 21 | dockerfile: ./05-multi-agent-workflow-k8s/services/wizard/Dockerfile 22 | -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-k8s/manifests/elf-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: elf 5 | namespace: default 6 | spec: 7 | replicas: 1 8 | selector: 9 | matchLabels: 10 | app: elf 11 | strategy: {} 12 | template: 13 | metadata: 14 | annotations: 15 | dapr.io/app-id: elf 16 | dapr.io/enabled: "true" 17 | dapr.io/metrics-port: "59196" 18 | dapr.io/unix-domain-socket-path: "" 19 | creationTimestamp: null 20 | labels: 21 | app: elf 22 | spec: 23 | containers: 24 | - env: 25 | - name: APP_CHANNEL_ADDRESS 26 | - name: DAPR_METRICS_PORT 27 | value: "59196" 28 | - name: DEBUG 29 | value: "true" 30 | - name: APP_ID 31 | value: elf 32 | envFrom: 33 | - secretRef: 34 | name: openai-secrets 35 | image: localhost:5001/elf:latest 36 | imagePullPolicy: Always 37 | name: elf 38 | resources: {} 39 | -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-k8s/manifests/hobbit-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: hobbit 5 | namespace: default 6 | spec: 7 | replicas: 1 8 | selector: 9 | matchLabels: 10 | app: hobbit 11 | strategy: {} 12 | template: 13 | metadata: 14 | annotations: 15 | dapr.io/app-id: hobbit 16 | dapr.io/enabled: "true" 17 | dapr.io/metrics-port: "59160" 18 | dapr.io/unix-domain-socket-path: "" 19 | creationTimestamp: null 20 | labels: 21 | app: hobbit 22 | spec: 23 | containers: 24 | - env: 25 | - name: APP_ID 26 | value: hobbit 27 | - name: APP_CHANNEL_ADDRESS 28 | - name: DAPR_METRICS_PORT 29 | value: "59160" 30 | - name: DEBUG 31 | value: "true" 32 | envFrom: 33 | - secretRef: 34 | name: openai-secrets 35 | image: localhost:5001/hobbit:latest 36 | imagePullPolicy: Always 37 | name: hobbit 38 | resources: {} 39 | -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-k8s/manifests/wizard-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: wizard 5 | namespace: default 6 | spec: 7 | replicas: 1 8 | selector: 9 | matchLabels: 10 | app: wizard 11 | strategy: {} 12 | template: 13 | metadata: 14 | annotations: 15 | dapr.io/app-id: wizard 16 | dapr.io/enabled: "true" 17 | dapr.io/metrics-port: "59074" 18 | dapr.io/unix-domain-socket-path: "" 19 | creationTimestamp: null 20 | labels: 21 | app: wizard 22 | spec: 23 | containers: 24 | - env: 25 | - name: DAPR_METRICS_PORT 26 | value: "59074" 27 | - name: DEBUG 28 | value: "true" 29 | - name: APP_ID 30 | value: wizard 31 | - name: APP_CHANNEL_ADDRESS 32 | envFrom: 33 | - secretRef: 34 | name: openai-secrets 35 | image: localhost:5001/wizard:latest 36 | imagePullPolicy: Always 37 | name: wizard 38 | resources: {} 39 | -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-k8s/manifests/workflow-llm-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: workflow-llm 5 | namespace: default 6 | spec: 7 | replicas: 1 8 | selector: 9 | matchLabels: 10 | app: workflow-llm 11 | strategy: {} 12 | template: 13 | metadata: 14 | annotations: 15 | dapr.io/app-id: workflow-llm 16 | dapr.io/app-port: "8004" 17 | dapr.io/enabled: "true" 18 | dapr.io/metrics-port: "59213" 19 | dapr.io/unix-domain-socket-path: "" 20 | creationTimestamp: null 21 | labels: 22 | app: workflow-llm 23 | spec: 24 | containers: 25 | - env: 26 | - name: APP_PORT 27 | value: "8004" 28 | - name: DAPR_METRICS_PORT 29 | value: "59213" 30 | - name: DEBUG 31 | value: "true" 32 | - name: APP_ID 33 | value: workflow-llm 34 | - name: APP_CHANNEL_ADDRESS 35 | envFrom: 36 | - secretRef: 37 | name: openai-secrets 38 | image: localhost:5001/workflow-llm:latest 39 | imagePullPolicy: Always 40 | name: workflow-llm 41 | ports: 42 | - containerPort: 8004 43 | resources: {} 44 | -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-k8s/manifests/workflow-llm-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | app: workflow-llm 6 | name: workflow-llm 7 | spec: 8 | ports: 9 | - port: 80 10 | protocol: TCP 11 | targetPort: 8004 12 | selector: 13 | app: workflow-llm 14 | type: ClusterIP 15 | -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-k8s/services/client/k8s_http_client.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import sys 3 | import requests 4 | import time 5 | 6 | 7 | def call_trigger_job(task): 8 | task_payload = {"task": task} 9 | base_url = "http://localhost:8004" 10 | workflow_url = f"{base_url}/start-workflow" 11 | status_url = f"{base_url}/status" 12 | 13 | for attempt in range(1, 11): 14 | try: 15 | print(f"Attempt {attempt}...") 16 | response = requests.get(status_url, timeout=5) 17 | 18 | if response.status_code == 200: 19 | print("Workflow app is healthy!") 20 | break 21 | else: 22 | print(f"Received status code {response.status_code}: {response.text}") 23 | 24 | except requests.exceptions.RequestException as e: 25 | print(f"Request failed: {e}") 26 | 27 | attempt += 1 28 | print("Waiting 5s seconds before next health checkattempt...") 29 | time.sleep(5) 30 | 31 | for attempt in range(1, 11): 32 | try: 33 | print(f"Attempt {attempt}...") 34 | response = requests.post(workflow_url, json=task_payload, timeout=5) 35 | 36 | if response.status_code == 202: 37 | print("Workflow started successfully!") 38 | sys.exit(0) 39 | else: 40 | print(f"Received status code {response.status_code}: {response.text}") 41 | 42 | except requests.exceptions.RequestException as e: 43 | print(f"Request failed: {e}") 44 | 45 | attempt += 1 46 | print("Waiting 1s seconds before next attempt...") 47 | time.sleep(1) 48 | 49 | print("Maximum attempts (10) reached without success.") 50 | 51 | print("Failed to get successful response") 52 | sys.exit(1) 53 | 54 | 55 | if __name__ == "__main__": 56 | task = "How to get to Mordor? We all need to help!" 57 | call_trigger_job(task) 58 | -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-k8s/services/client/requirements.txt: -------------------------------------------------------------------------------- 1 | requests -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-k8s/services/elf/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:12-slim@sha256:1209d8fd77def86ceb6663deef7956481cc6c14a25e1e64daec12c0ceffcc19d AS build 2 | RUN apt-get update && \ 3 | apt-get install --no-install-suggests --no-install-recommends --yes python3-venv gcc libpython3-dev && \ 4 | python3.11 -m venv /venv && \ 5 | /venv/bin/pip install --upgrade pip setuptools wheel 6 | 7 | # Build the virtualenv as a separate step: Only re-execute this step when requirements.txt changes 8 | FROM build AS build-venv 9 | COPY ./05-multi-agent-workflow-dapr-workflows/requirements.txt /requirements.txt 10 | RUN /venv/bin/pip install --disable-pip-version-check -r /requirements.txt 11 | 12 | # Copy the virtualenv into a distroless image 13 | FROM gcr.io/distroless/python3-debian12@sha256:7729c7979e1097d2b60fffb0af904a59e16af241b7c8eef4c613013ef1504ff8 14 | COPY --from=build-venv /venv /venv 15 | COPY ./05-multi-agent-workflow-dapr-workflows/services/elf/app.py /app/app.py 16 | WORKDIR /app 17 | 18 | ENTRYPOINT ["/venv/bin/python3", "app.py"] 19 | -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-k8s/services/hobbit/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:12-slim@sha256:1209d8fd77def86ceb6663deef7956481cc6c14a25e1e64daec12c0ceffcc19d AS build 2 | RUN apt-get update && \ 3 | apt-get install --no-install-suggests --no-install-recommends --yes python3-venv gcc libpython3-dev && \ 4 | python3.11 -m venv /venv && \ 5 | /venv/bin/pip install --upgrade pip setuptools wheel 6 | 7 | # Build the virtualenv as a separate step: Only re-execute this step when requirements.txt changes 8 | FROM build AS build-venv 9 | COPY ./05-multi-agent-workflow-dapr-workflows/requirements.txt /requirements.txt 10 | RUN /venv/bin/pip install --disable-pip-version-check -r /requirements.txt 11 | 12 | # Copy the virtualenv into a distroless image 13 | FROM gcr.io/distroless/python3-debian12@sha256:7729c7979e1097d2b60fffb0af904a59e16af241b7c8eef4c613013ef1504ff8 14 | COPY --from=build-venv /venv /venv 15 | COPY ./05-multi-agent-workflow-dapr-workflows/services/hobbit/app.py /app/app.py 16 | WORKDIR /app 17 | 18 | ENTRYPOINT ["/venv/bin/python3", "app.py"] 19 | -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-k8s/services/wizard/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:12-slim@sha256:1209d8fd77def86ceb6663deef7956481cc6c14a25e1e64daec12c0ceffcc19d AS build 2 | RUN apt-get update && \ 3 | apt-get install --no-install-suggests --no-install-recommends --yes python3-venv gcc libpython3-dev && \ 4 | python3.11 -m venv /venv && \ 5 | /venv/bin/pip install --upgrade pip setuptools wheel 6 | 7 | # Build the virtualenv as a separate step: Only re-execute this step when requirements.txt changes 8 | FROM build AS build-venv 9 | COPY ./05-multi-agent-workflow-dapr-workflows/requirements.txt /requirements.txt 10 | RUN /venv/bin/pip install --disable-pip-version-check -r /requirements.txt 11 | 12 | # Copy the virtualenv into a distroless image 13 | FROM gcr.io/distroless/python3-debian12@sha256:7729c7979e1097d2b60fffb0af904a59e16af241b7c8eef4c613013ef1504ff8 14 | COPY --from=build-venv /venv /venv 15 | COPY ./05-multi-agent-workflow-dapr-workflows/services/wizard/app.py /app/app.py 16 | WORKDIR /app 17 | 18 | ENTRYPOINT ["/venv/bin/python3", "app.py"] 19 | -------------------------------------------------------------------------------- /quickstarts/05-multi-agent-workflow-k8s/services/workflow-llm/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:12-slim@sha256:1209d8fd77def86ceb6663deef7956481cc6c14a25e1e64daec12c0ceffcc19d AS build 2 | RUN apt-get update && \ 3 | apt-get install --no-install-suggests --no-install-recommends --yes python3-venv gcc libpython3-dev && \ 4 | python3.11 -m venv /venv && \ 5 | /venv/bin/pip install --upgrade pip setuptools wheel 6 | 7 | # Build the virtualenv as a separate step: Only re-execute this step when requirements.txt changes 8 | FROM build AS build-venv 9 | COPY ./05-multi-agent-workflow-dapr-workflows/requirements.txt /requirements.txt 10 | RUN /venv/bin/pip install --disable-pip-version-check -r /requirements.txt 11 | 12 | # Copy the virtualenv into a distroless image 13 | FROM gcr.io/distroless/python3-debian12@sha256:7729c7979e1097d2b60fffb0af904a59e16af241b7c8eef4c613013ef1504ff8 14 | COPY --from=build-venv /venv /venv 15 | COPY ./05-multi-agent-workflow-dapr-workflows/services/workflow-llm/app.py /app/app.py 16 | WORKDIR /app 17 | 18 | ENTRYPOINT ["/venv/bin/python3", "app.py"] 19 | -------------------------------------------------------------------------------- /quickstarts/06-document-agent-chainlit/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Virtual environments 10 | .venv/ 11 | venv/ 12 | ENV/ 13 | env/ 14 | .pipenv/ 15 | *.egg-info/ 16 | .eggs/ 17 | 18 | # Distribution / packaging 19 | build/ 20 | develop-eggs/ 21 | dist/ 22 | downloads/ 23 | eggs/ 24 | lib/ 25 | lib64/ 26 | parts/ 27 | sdist/ 28 | var/ 29 | *.egg 30 | MANIFEST 31 | 32 | # PyInstaller 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | 52 | # Pytest 53 | .pytest_cache/ 54 | 55 | # Jupyter Notebook 56 | .ipynb_checkpoints 57 | 58 | # pyenv 59 | .python-version 60 | 61 | # VS Code 62 | .vscode/ 63 | 64 | # mypy 65 | .mypy_cache/ 66 | .dmypy.json 67 | dmypy.json 68 | 69 | # Pyre type checker 70 | .pyre/ 71 | 72 | # Pylint 73 | .pylint.d/ 74 | 75 | # IDEs and editors 76 | .idea/ 77 | *.sublime-workspace 78 | *.sublime-project 79 | *.vscode/ 80 | 81 | # MacOS 82 | .DS_Store 83 | 84 | # Logs 85 | *.log 86 | .files/ 87 | 88 | # Local environment variables 89 | .env 90 | .env.* 91 | 92 | # Docker 93 | *.pid 94 | 95 | # Chainlit 96 | .chainlit/ 97 | chainlit.md 98 | -------------------------------------------------------------------------------- /quickstarts/06-document-agent-chainlit/components/conversationmemory.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: conversationstore 5 | spec: 6 | type: state.redis 7 | version: v1 8 | metadata: 9 | - name: redisHost 10 | value: localhost:6379 11 | - name: redisPassword 12 | value: "" 13 | -------------------------------------------------------------------------------- /quickstarts/06-document-agent-chainlit/components/filestorage.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: upload 5 | spec: 6 | type: bindings.aws.s3 7 | version: v1 8 | metadata: 9 | - name: bucket 10 | value: 11 | - name: region 12 | value: 13 | - name: accessKey 14 | value: 15 | - name: secretKey 16 | value: 17 | -------------------------------------------------------------------------------- /quickstarts/06-document-agent-chainlit/red_foxes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapr/dapr-agents/c2eff2b9710b3ee7021b5d2f0f11a5994c88d687/quickstarts/06-document-agent-chainlit/red_foxes.pdf -------------------------------------------------------------------------------- /quickstarts/06-document-agent-chainlit/requirements.txt: -------------------------------------------------------------------------------- 1 | dapr-agents>=0.5.0 2 | python-dotenv 3 | requests 4 | chainlit 5 | unstructured[all-docs] -------------------------------------------------------------------------------- /quickstarts/07-agent-mcp-client-sse/app.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import logging 3 | from dotenv import load_dotenv 4 | 5 | from dapr_agents import AssistantAgent 6 | from dapr_agents.tool.mcp import MCPClient 7 | 8 | 9 | async def main(): 10 | try: 11 | # Load MCP tools from server (stdio or sse) 12 | client = MCPClient() 13 | await client.connect_sse("local", url="http://localhost:8000/sse") 14 | 15 | # Convert MCP tools to AgentTool list 16 | tools = client.get_all_tools() 17 | 18 | # Create the Weather Agent using those tools 19 | weather_agent = AssistantAgent( 20 | role="Weather Assistant", 21 | name="Stevie", 22 | goal="Help humans get weather and location info using smart tools.", 23 | instructions=[ 24 | "Respond clearly and helpfully to weather-related questions.", 25 | "Use tools when appropriate to fetch or simulate weather data.", 26 | "You may sometimes jump after answering the weather question.", 27 | ], 28 | tools=tools, 29 | message_bus_name="messagepubsub", 30 | state_store_name="workflowstatestore", 31 | state_key="workflow_state", 32 | agents_registry_store_name="agentstatestore", 33 | agents_registry_key="agents_registry", 34 | ).as_service(port=8001) 35 | 36 | # Start the FastAPI agent service 37 | await weather_agent.start() 38 | 39 | except Exception as e: 40 | logging.exception("Error starting weather agent service", exc_info=e) 41 | 42 | 43 | if __name__ == "__main__": 44 | load_dotenv() 45 | logging.basicConfig(level=logging.INFO) 46 | asyncio.run(main()) 47 | -------------------------------------------------------------------------------- /quickstarts/07-agent-mcp-client-sse/components/pubsub.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: messagepubsub 5 | spec: 6 | type: pubsub.redis 7 | version: v1 8 | metadata: 9 | - name: redisHost 10 | value: localhost:6379 11 | - name: redisPassword 12 | value: "" -------------------------------------------------------------------------------- /quickstarts/07-agent-mcp-client-sse/components/statestore.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: agentstatestore 5 | spec: 6 | type: state.redis 7 | version: v1 8 | metadata: 9 | - name: redisHost 10 | value: localhost:6379 11 | - name: redisPassword 12 | value: "" 13 | - name: keyPrefix 14 | value: none 15 | - name: actorStateStore 16 | value: "true" -------------------------------------------------------------------------------- /quickstarts/07-agent-mcp-client-sse/components/workflowstate.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: workflowstatestore 5 | spec: 6 | type: state.redis 7 | version: v1 8 | metadata: 9 | - name: redisHost 10 | value: localhost:6379 11 | - name: redisPassword 12 | value: "" -------------------------------------------------------------------------------- /quickstarts/07-agent-mcp-client-sse/requirements.txt: -------------------------------------------------------------------------------- 1 | dapr-agents>=0.5.0 2 | python-dotenv 3 | mcp 4 | starlette 5 | uvicorn 6 | requests -------------------------------------------------------------------------------- /quickstarts/07-agent-mcp-client-sse/tools.py: -------------------------------------------------------------------------------- 1 | from mcp.server.fastmcp import FastMCP 2 | import random 3 | 4 | mcp = FastMCP("TestServer") 5 | 6 | 7 | @mcp.tool() 8 | async def get_weather(location: str) -> str: 9 | """Get weather information for a specific location.""" 10 | temperature = random.randint(60, 80) 11 | return f"{location}: {temperature}F." 12 | 13 | 14 | @mcp.tool() 15 | async def jump(distance: str) -> str: 16 | """Simulate a jump of a given distance.""" 17 | return f"I jumped the following distance: {distance}" 18 | -------------------------------------------------------------------------------- /quickstarts/07-agent-mcp-client-stdio/agent.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import logging 3 | import sys 4 | from dotenv import load_dotenv 5 | 6 | from dapr_agents import Agent 7 | from dapr_agents.tool.mcp import MCPClient 8 | 9 | load_dotenv() 10 | 11 | 12 | async def main(): 13 | # Create the MCP client 14 | client = MCPClient() 15 | 16 | # Connect to MCP server using STDIO transport 17 | await client.connect_stdio( 18 | server_name="local", 19 | command=sys.executable, # Use the current Python interpreter 20 | args=["tools.py"], # Run tools.py directly 21 | ) 22 | 23 | # Get available tools from the MCP instance 24 | tools = client.get_all_tools() 25 | print("🔧 Available tools:", [t.name for t in tools]) 26 | 27 | # Create the Weather Agent using MCP tools 28 | weather_agent = Agent( 29 | name="Stevie", 30 | role="Weather Assistant", 31 | goal="Help humans get weather and location info using MCP tools.", 32 | instructions=[ 33 | "Respond clearly and helpfully to weather-related questions.", 34 | "Use tools when appropriate to fetch or simulate weather data.", 35 | "You may sometimes jump after answering the weather question.", 36 | ], 37 | tools=tools, 38 | ) 39 | 40 | # Run a sample query 41 | result = await weather_agent.run("What is the weather in New York?") 42 | print(result) 43 | 44 | # Clean up resources 45 | await client.close() 46 | 47 | 48 | if __name__ == "__main__": 49 | asyncio.run(main()) 50 | -------------------------------------------------------------------------------- /quickstarts/07-agent-mcp-client-stdio/requirements.txt: -------------------------------------------------------------------------------- 1 | dapr-agents>=0.5.0 2 | python-dotenv 3 | mcp -------------------------------------------------------------------------------- /quickstarts/07-agent-mcp-client-stdio/tools.py: -------------------------------------------------------------------------------- 1 | from mcp.server.fastmcp import FastMCP 2 | import random 3 | 4 | mcp = FastMCP("TestServer") 5 | 6 | 7 | @mcp.tool() 8 | async def get_weather(location: str) -> str: 9 | """Get weather information for a specific location.""" 10 | temperature = random.randint(60, 80) 11 | return f"{location}: {temperature}F." 12 | 13 | 14 | @mcp.tool() 15 | async def jump(distance: str) -> str: 16 | """Simulate a jump of a given distance.""" 17 | return f"I jumped the following distance: {distance}" 18 | 19 | 20 | # When run directly, serve tools over STDIO 21 | if __name__ == "__main__": 22 | mcp.run("stdio") 23 | -------------------------------------------------------------------------------- /quickstarts/08-data-agent-mcp-chainlit/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Virtual environments 10 | .venv/ 11 | venv/ 12 | ENV/ 13 | env/ 14 | .pipenv/ 15 | *.egg-info/ 16 | .eggs/ 17 | 18 | # Distribution / packaging 19 | build/ 20 | develop-eggs/ 21 | dist/ 22 | downloads/ 23 | eggs/ 24 | lib/ 25 | lib64/ 26 | parts/ 27 | sdist/ 28 | var/ 29 | *.egg 30 | MANIFEST 31 | 32 | # PyInstaller 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | 52 | # Pytest 53 | .pytest_cache/ 54 | 55 | # Jupyter Notebook 56 | .ipynb_checkpoints 57 | 58 | # pyenv 59 | .python-version 60 | 61 | # VS Code 62 | .vscode/ 63 | 64 | # mypy 65 | .mypy_cache/ 66 | .dmypy.json 67 | dmypy.json 68 | 69 | # Pyre type checker 70 | .pyre/ 71 | 72 | # Pylint 73 | .pylint.d/ 74 | 75 | # IDEs and editors 76 | .idea/ 77 | *.sublime-workspace 78 | *.sublime-project 79 | *.vscode/ 80 | 81 | # MacOS 82 | .DS_Store 83 | 84 | # Logs 85 | *.log 86 | .files/ 87 | 88 | # Local environment variables 89 | .env 90 | .env.* 91 | 92 | # Docker 93 | *.pid 94 | 95 | # Chainlit 96 | .chainlit/ 97 | chainlit.md 98 | -------------------------------------------------------------------------------- /quickstarts/08-data-agent-mcp-chainlit/components/conversationmemory.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: dapr.io/v1alpha1 2 | kind: Component 3 | metadata: 4 | name: conversationstore 5 | spec: 6 | type: state.redis 7 | version: v1 8 | metadata: 9 | - name: redisHost 10 | value: localhost:6379 11 | - name: redisPassword 12 | value: "" 13 | -------------------------------------------------------------------------------- /quickstarts/08-data-agent-mcp-chainlit/requirements.txt: -------------------------------------------------------------------------------- 1 | dapr-agents>=0.5.1 2 | python-dotenv 3 | chainlit 4 | psycopg 5 | psycopg[binary] -------------------------------------------------------------------------------- /quickstarts/08-data-agent-mcp-chainlit/schema.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS users; 2 | 3 | CREATE TABLE users ( 4 | id SERIAL PRIMARY KEY, 5 | name VARCHAR(100) NOT NULL, 6 | email VARCHAR(255) UNIQUE NOT NULL, 7 | is_customer BOOLEAN DEFAULT FALSE, 8 | churn_reason TEXT, 9 | created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP 10 | ); 11 | -------------------------------------------------------------------------------- /quickstarts/08-data-agent-mcp-chainlit/users.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO users (name, email, is_customer, churn_reason, created_at) VALUES 2 | ('Alice Johnson', 'alice@example.com', TRUE, NULL, NOW() - INTERVAL '6 months'), 3 | ('Bob Smith', 'bob@example.com', TRUE, NULL, NOW() - INTERVAL '5 months'), 4 | ('Carla Ruiz', 'carla@example.com', TRUE, NULL, NOW() - INTERVAL '4 months'), 5 | ('David Lee', 'david@example.com', TRUE, NULL, NOW() - INTERVAL '3 months'), 6 | ('Emma Chen', 'emma@example.com', TRUE, NULL, NOW() - INTERVAL '2 months'), 7 | ('Frank Novak', 'frank@example.com', FALSE, 'Spent 10 minutes trying to find the logout button — it was hidden in a weird place.', NOW() - INTERVAL '6 weeks'), 8 | ('Grace Patel', 'grace@example.com', FALSE, 'The dashboard felt cluttered and overwhelming right from the start.', NOW() - INTERVAL '5 weeks'), 9 | ('Hassan Ali', 'hassan@example.com', FALSE, 'Couldn’t figure out how to edit my profile — had to Google it.', NOW() - INTERVAL '4 weeks'), 10 | ('Isabella Moreau', 'isabella@example.com', FALSE, 'Forms had way too many fields and no clear labels.', NOW() - INTERVAL '3 weeks'), 11 | ('Jamal Wright', 'jamal@example.com', FALSE, 'Nothing looked clickable — I was stuck on the home screen for a while.', NOW() - INTERVAL '2 weeks'); 12 | -------------------------------------------------------------------------------- /quickstarts/validate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | echo "Home: $HOME" 3 | 4 | cd $1 && mm.py -l README.md 5 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pydantic==2.11.3 2 | openai==1.75.0 3 | openapi-pydantic==0.5.1 4 | openapi-schema-pydantic==1.2.4 5 | regex>=2023.12.25 6 | Jinja2>=3.1.6 7 | azure-identity==1.21.0 8 | dapr>=1.15.0 9 | dapr-ext-fastapi==1.15.0 10 | dapr-ext-workflow==1.15.0 11 | colorama==0.4.6 12 | cloudevents==1.11.0 13 | pyyaml==6.0.2 14 | rich==13.9.4 15 | huggingface_hub==0.30.2 16 | numpy==2.2.2 17 | mcp==1.7.1 18 | opentelemetry-distro==0.53b1 19 | opentelemetry-exporter-otlp==1.32.1 20 | opentelemetry-instrumentation-requests==0.53b1 -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | skipsdist = True 3 | minversion = 3.9.0 4 | envlist = 5 | py{39,310,311,312,313} 6 | flake8, 7 | ruff, 8 | mypy, 9 | 10 | [testenv:flake8] 11 | basepython = python3 12 | usedevelop = False 13 | deps = flake8 14 | commands = 15 | flake8 . --ignore=E501,F401,W503,E203 16 | 17 | [testenv:ruff] 18 | basepython = python3 19 | usedevelop = False 20 | deps = ruff==0.2.2 21 | commands = 22 | ruff format 23 | 24 | [testenv:type] 25 | basepython = python3 26 | usedevelop = False 27 | deps = -rdev-requirements.txt 28 | commands = 29 | mypy --config-file mypy.ini 30 | --------------------------------------------------------------------------------