From 588fdc0686ff444ba1ae68e0c6cee60f6564327a Mon Sep 17 00:00:00 2001 From: yaron2 Date: Fri, 15 Aug 2025 13:52:36 -0700 Subject: [PATCH 1/6] add agentic workflow quickstart Signed-off-by: yaron2 --- AI/agentic_workflows/.gitignore | 66 ++++++++ AI/agentic_workflows/README.md | 114 +++++++++++++ AI/agentic_workflows/anthropic/README.md | 121 ++++++++++++++ AI/agentic_workflows/anthropic/app.py | 149 +++++++++++++++++ .../anthropic/requirements.txt | 3 + AI/agentic_workflows/gemini/README.md | 121 ++++++++++++++ AI/agentic_workflows/gemini/app.py | 157 ++++++++++++++++++ AI/agentic_workflows/gemini/requirements.txt | 3 + AI/agentic_workflows/openai/README.md | 121 ++++++++++++++ AI/agentic_workflows/openai/app.py | 157 ++++++++++++++++++ AI/agentic_workflows/openai/requirements.txt | 3 + README.md | 3 +- 12 files changed, 1017 insertions(+), 1 deletion(-) create mode 100644 AI/agentic_workflows/.gitignore create mode 100644 AI/agentic_workflows/README.md create mode 100644 AI/agentic_workflows/anthropic/README.md create mode 100644 AI/agentic_workflows/anthropic/app.py create mode 100644 AI/agentic_workflows/anthropic/requirements.txt create mode 100644 AI/agentic_workflows/gemini/README.md create mode 100644 AI/agentic_workflows/gemini/app.py create mode 100644 AI/agentic_workflows/gemini/requirements.txt create mode 100644 AI/agentic_workflows/openai/README.md create mode 100644 AI/agentic_workflows/openai/app.py create mode 100644 AI/agentic_workflows/openai/requirements.txt diff --git a/AI/agentic_workflows/.gitignore b/AI/agentic_workflows/.gitignore new file mode 100644 index 000000000..96dec6080 --- /dev/null +++ b/AI/agentic_workflows/.gitignore @@ -0,0 +1,66 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets- diff --git a/AI/agentic_workflows/README.md b/AI/agentic_workflows/README.md new file mode 100644 index 000000000..7375c51d4 --- /dev/null +++ b/AI/agentic_workflows/README.md @@ -0,0 +1,114 @@ +## 🧠 Agentic Contract Review β€” Durable Workflows with Dapr (+ OpenAI, Gemini, Anthropic) + +This repo demonstrates a durable, agentic AI workflow for contract review built on Dapr Workflows and a simple FastAPI service. It ships three interchangeable implementations using different LLM providers: + +* `./anthropic/` β€” Anthropic Claude–based contract analyst +* `./gemini/` β€” Google Gemini–based contract analyst +* `./openai/` β€” OpenAI-based contract analyst + +Each example uses the same workflow shape: **analyze β†’ decide β†’ (optionally) wait for human approval β†’ finalize β†’ report**. The only difference is the LLM client and prompts used under the hood. + +Start in the provider folder you want (e.g., cd gemini) and follow that README. + +### ✨ What the Agentic Workflow Does + +* **LLM analysis**: Extract risks and summarize the contract. +* **Decision**: If risk > threshold, propose amendments and pause for human approval. +* **Durability**: The workflow persists state and can survive restarts and timeouts. +* **Output**: Emit a structured JSON report for downstream systems. + + +All three implementations share these semantics; you can swap providers without changing orchestration logic. + +### 🧩 Tech Highlights + +* **Dapr Workflows**: Durable execution, timers, retries, external event waiting. +* **FastAPI**: Minimal HTTP surface for starting workflows and posting approvals. + +### πŸ›  Prerequisites (All Providers) + +* Python 3.10+ +* [Dapr and Dapr Cli](https://docs.dapr.io/getting-started/install-dapr-cli/) +* Docker (for local Dapr dependencies) +* Provider API key (one of): + * **OpenAI**: OPENAI\_API\_KEY + * **Gemini**: GEMINI\_API\_KEY + * **Anthropic**: ANTHROPIC\_API\_KEY + +Each subfolder has its own requirements.txt and provider-specific notes. + +### πŸš€ Quickstart + +Pick a provider folder and follow its README. The flow is generally: + +```bash +# 1) Choose a provider implementation +cd gemini # or openai / anthropic + +# 2) Install deps +pip install -r requirements.txt + +# 3) Export your provider API key +export OPENAI_API_KEY=... // or GEMINI_API_KEY or ANTHROPIC_API_KEY + +# 4) Run the FastAPI app with Dapr +dapr run --app-id contract-review --app-port 8000 -- uvicorn app:app --reload + +# 5) Kick off a contract review (example payloads in each README) +curl -X POST http://localhost:8000/review -H "Content-Type: application/json" -d '{ "...": "..." }' + +# 6) If required, approve later by posting to /approve/{instanceId} + +```bash +curl -X POST http://localhost:8000/approve/workflow- -H "Content-Type: application/json" -d '{ + "approved": true, + "reviewer": "Alice", + "notes": "Reviewed and approved." +}' +``` + +### πŸ”„ Choosing a Provider + +Start with the provider you already use in production or prefer for pricing/latency. + +The workflow contract (endpoints, payloads, and output JSON) is held constant across implementations to make A/B comparisons trivial. + +#### πŸ§ͺ Example Endpoints (common shape) + +* POST /review β€” Start a new workflow instance with a contract payload +* POST /approve/{instanceId} β€” Resume a paused workflow with human approval + +See each provider's README for exact payloads and sample cURL commands. + +#### 🧱 Architecture (High-Level) + +```yaml +[Client] ──HTTP──> [FastAPI + Dapr Workflow] + β”‚ + β”œβ”€ Activity: LLM Analysis (OpenAI/Gemini/Anthropic) + β”œβ”€ Timer/Wait: Human Approval (external event) + └─ Activity: Finalize Report β†’ JSON +``` + +* **Durability**: Dapr Workflow state persists in the configured state store. +* **Resilience**: Retries and timers are handled by the workflow runtime. +* **Extensibility**: Add tools (RAG, DB lookups, signature checks) as new activities. + +### 🧯 Troubleshooting + +* Run dapr status to confirm the sidecar and default components are healthy. +* If workflow doesn't resume, verify you're posting approval to the correct instance ID. +* If the workflow doesn't kick off, make sure you're using a new instance ID in the /review request. +* Check provider-specific rate limits or key scopes. +* Inspect app logs (and Dapr sidecar logs) for activity failures and retries. + +### πŸ“š Related Docs +-------------------------------- + +* **Dapr Workflows**: [https://docs.dapr.io/developing-applications/building-blocks/workflow/](https://docs.dapr.io/developing-applications/building-blocks/workflow/) +* **OpenAI Python SDK**: [https://platform.openai.com/docs/](https://platform.openai.com/docs/) +* **Google Gemini**: [https://aistudio.google.com/](https://aistudio.google.com/) +* **Anthropic Claude**: [https://docs.anthropic.com/](https://docs.anthropic.com/) + +
+Built with ❀️ using Dapr Workflows. Swap the model, keep the durability. \ No newline at end of file diff --git a/AI/agentic_workflows/anthropic/README.md b/AI/agentic_workflows/anthropic/README.md new file mode 100644 index 000000000..e68113330 --- /dev/null +++ b/AI/agentic_workflows/anthropic/README.md @@ -0,0 +1,121 @@ +# 🧠 Agentic Contract Review Workflow (Dapr + Claude + FastAPI) + +This project demonstrates how to build a **durable, agentic AI workflow** using [Dapr Workflows](https://docs.dapr.io/developing-applications/building-blocks/workflow/) and [Anthropic Claude](https://www.anthropic.com/claude). It showcases how to mix the intelligence of a large language model with the resilience and statefulness of a distributed workflow engine. + +--- + +## ✨ What It Does + +Claude plays the role of a contract analyst, and Dapr Workflows orchestrates the process across time: + +1. Claude reviews each contract and identifies risky clauses. +2. If the contract is risky, Claude drafts amendments. +3. The workflow **waits for human approval** β€” with timeout and persistence. +4. The results are compiled into a final structured report. + +Everything runs in a clean FastAPI service using Dapr's durable execution engine underneath. + +Even though this example is using Claude, you can replace the Anthropic SDK with any other LLM provider. + +--- + +## πŸ›  Prerequisites + +- Python 3.10+ +- [Dapr and Dapr Cli](https://docs.dapr.io/getting-started/install-dapr-cli/) +- Docker (for Redis state backend via `dapr init`) +- Anthropic Claude API Key (sign up at [Anthropic](https://www.anthropic.com/)) + +Install dependencies: + +```bash +pip install -r requirements.txt +``` + +--- + +## πŸš€ Running the App + +### 1. Start the Dapr-enabled FastAPI server + +```bash +dapr run --app-id contract-review --app-port 8000 -- uvicorn app:app --reload +``` + +This launches the FastAPI app with Dapr Workflow runtime. You’ll see: + +``` +== Dapr Workflow Runtime started == +``` + +--- + +### 2. Submit a contract for review + +For subsequent runs, make sure to use a new `id`. + +```bash +curl -X POST http://localhost:8000/review \ + -H "Content-Type: application/json" \ + -d '{ + "id": "C100", + "text": "SERVICE AGREEMENT\n\nThis Service Agreement (\"Agreement\") is entered into between ACME Data Solutions, Inc. (\"Provider\") and ClientCo LLC (\"Client\") effective as of August 1, 2025.\n\n1. SERVICES\nProvider agrees to deliver cloud data analytics services to Client as described in Exhibit A.\n\n2. PAYMENT TERMS\nClient shall pay Provider $50,000 per month, due within 15 days of invoice date. Failure to pay within 30 days may result in suspension of services.\n\n3. DATA OWNERSHIP\nAll data processed shall become the property of Provider, including any derivative works, without limitation.\n\n4. LIABILITY LIMITATION\nProvider shall not be liable for any damages, including loss of revenue, indirect, incidental, or consequential damages, even if advised of the possibility thereof.\n\n5. TERMINATION\nEither party may terminate this Agreement at any time with 5 days’ written notice. Client shall remain responsible for payment for all services rendered up to the termination date.\n\n6. CONFIDENTIALITY\nBoth parties agree to maintain confidentiality of proprietary information for a period of 12 months following termination.\n\nIN WITNESS WHEREOF, the parties have executed this Agreement as of the date first written above." +}' +``` + +This creates a new durable workflow instance: `workflow-C100`. + +When starting future contract approval workflows, use a new workflow instance id for every call. + +--- + +### 3. (Optional) Approve the contract if required + +If Claude flags the contract as high risk, the workflow pauses and waits for this: + +```bash +curl -X POST http://localhost:8000/approve/workflow-C100 -H "Content-Type: application/json" -d '{ + "approved": true, + "reviewer": "Alice", + "notes": "Reviewed and approved as-is." +}' +``` + +If approval is not received in 24 hours, the workflow times out and continues gracefully. + +--- + +## 🧠 Why Use Dapr Workflows? + +Traditional LLM apps forget things, fail silently, and can’t wait. + +Dapr Workflows gives your agent: + +- **Durability** β€” survives restarts and crashes +- **Event waiting** β€” pauses for human input or external triggers +- **Retries & orchestration** β€” handles timeouts, branches, parallel execution +- **Composability** β€” plug in tools, APIs, databases, and humans + +--- + +## πŸ§ͺ Example Output + +```json +{ + "id": "C100", + "analysis": { + "summary": "The contract includes broad data ownership and limited liability.", + "risk_score": 82, + "risky_clauses": ["3. Data Ownership", "4. Liability Limitation"] + }, + "approved": true, + "approver": "Alice", + "notes": "Reviewed and approved as-is." +} +``` + +--- + +## πŸ“š Resources + +- [🧭 Dapr Workflow Documentation](https://docs.dapr.io/developing-applications/building-blocks/workflow/workflow-overview/) \ No newline at end of file diff --git a/AI/agentic_workflows/anthropic/app.py b/AI/agentic_workflows/anthropic/app.py new file mode 100644 index 000000000..547f2d4dd --- /dev/null +++ b/AI/agentic_workflows/anthropic/app.py @@ -0,0 +1,149 @@ +from fastapi import FastAPI +from pydantic import BaseModel +from contextlib import asynccontextmanager +from dapr.ext.workflow.workflow_runtime import WorkflowRuntime +from dapr.ext.workflow import DaprWorkflowClient +from datetime import timedelta +import dapr.ext.workflow as wf + +import os, json, time +from dataclasses import dataclass +import anthropic + +wfr = WorkflowRuntime() + +# === Anthropic Setup === +ANTHROPIC_API_KEY = os.getenv("ANTHROPIC_API_KEY") +client = anthropic.Anthropic(api_key=ANTHROPIC_API_KEY) + +@dataclass +class Approval: + approved: bool + reviewer: str + notes: str + + @staticmethod + def from_dict(dict): + return Approval(**dict) + +# === Activities === +def analyze_contract(ctx, contract): + prompt = f""" +You are a contract analysis assistant. +Summarize key clauses and identify potential risks. +Return JSON: summary, risk_score (0-100), risky_clauses. +Contract: +{contract['text']} +""" + resp = client.messages.create( + model="claude-3-5-sonnet-20240620", + max_tokens=800, + messages=[{"role": "user", "content": prompt}] + ) + try: + return json.loads(resp.content[0].text) + except: + return { + "summary": resp.content[0].text, + "risk_score": 50, + "risky_clauses": [] + } + +def draft_amendment(ctx, clauses): + prompt = f"Draft legally sound amendments for:\n{clauses}" + resp = client.messages.create( + model="claude-3-5-sonnet-20240620", + max_tokens=800, + messages=[{"role": "user", "content": prompt}] + ) + return resp.content[0].text + +def send_for_approval(ctx, amendment): + print(f"[Send] Amendment sent: {amendment}...", flush=True) + time.sleep(1) + return {"sent": True} + +def final_report(ctx, all_results): + return json.dumps(all_results, indent=2) + +# === Workflow === +def contract_review_wf(ctx: wf.DaprWorkflowContext, contracts: list): + results = [] + tasks = [ctx.call_activity(analyze_contract, input=contract) for contract in contracts] + analyses = yield wf.when_all(tasks) + + for contract, analysis in zip(contracts, analyses): + result = {"id": contract["id"], "analysis": analysis} + + if analysis["risk_score"] > 70: + amendment = yield ctx.call_activity(draft_amendment, input=analysis["risky_clauses"]) + yield ctx.call_activity(send_for_approval, input=amendment) + + print("Waiting on approval for contract " + contract['id'], flush=True) + approval_event = ctx.wait_for_external_event("contract_approval") + timer = ctx.create_timer(timedelta(hours=24)) + completed_task = yield wf.when_any([approval_event, timer]) + + if completed_task == approval_event: + event_result = Approval.from_dict(approval_event.get_result()) + result["approved"] = event_result.approved + result['approver'] = event_result.reviewer + result['notes'] = event_result.notes + else: + result["approved"] = False + + results.append(result) + + report = yield ctx.call_activity(final_report, input=results) + print(report, flush=True) + return report + +# === FastAPI Lifespan === +@asynccontextmanager +async def lifespan(app: FastAPI): + wfr.register_workflow(contract_review_wf) + wfr.register_activity(analyze_contract) + wfr.register_activity(draft_amendment) + wfr.register_activity(send_for_approval) + wfr.register_activity(final_report) + + wfr.start() + print("== Dapr Workflow Runtime started ==") + yield + wfr.shutdown() + print("== Dapr Workflow Runtime stopped ==") + +app = FastAPI(lifespan=lifespan) + +# === API === +class ContractInput(BaseModel): + id: str + text: str + +class ApprovalInput(BaseModel): + approved: bool + reviewer: str + notes: str + +@app.post("/review") +def start_contract_review(contract: ContractInput): + client = DaprWorkflowClient() + instance_id = f"workflow-{contract.id}" + workflow_input = [{"id": contract.id, "text": contract.text}] + + scheduled_id = client.schedule_new_workflow( + workflow=contract_review_wf, + input=workflow_input, + instance_id=instance_id + ) + return {"instance_id": scheduled_id, "status": "started"} + +@app.post("/approve/{instance_id}") +def approve_contract(instance_id: str, approval: ApprovalInput): + client = DaprWorkflowClient() + client.raise_workflow_event( + instance_id=instance_id, + event_name="contract_approval", + data=approval.model_dump() + ) + return {"status": "approval sent", "instance_id": instance_id} diff --git a/AI/agentic_workflows/anthropic/requirements.txt b/AI/agentic_workflows/anthropic/requirements.txt new file mode 100644 index 000000000..05de7d0d3 --- /dev/null +++ b/AI/agentic_workflows/anthropic/requirements.txt @@ -0,0 +1,3 @@ +dapr-ext-workflow +anthropic +uvicorn \ No newline at end of file diff --git a/AI/agentic_workflows/gemini/README.md b/AI/agentic_workflows/gemini/README.md new file mode 100644 index 000000000..0d62752b3 --- /dev/null +++ b/AI/agentic_workflows/gemini/README.md @@ -0,0 +1,121 @@ +# 🧠 Agentic Contract Review Workflow (Dapr + Google Gemini + FastAPI) + +This project demonstrates how to build a **durable, agentic AI workflow** using [Dapr Workflows](https://docs.dapr.io/developing-applications/building-blocks/workflow/) and [Google Gemini](https://aistudio.google.com/). It showcases how to mix the intelligence of a large language model with the resilience and statefulness of a distributed workflow engine. + +--- + +## ✨ What It Does + +Gemini plays the role of a contract analyst, and Dapr Workflows orchestrates the process across time: + +1. Gemini reviews each contract and identifies risky clauses. +2. If the contract is risky, Gemini drafts amendments. +3. The workflow **waits for human approval** β€” with timeout and persistence. +4. The results are compiled into a final structured report. + +Everything runs in a clean FastAPI service using Dapr's durable execution engine underneath. + +Even though this example is using Gemini, you can replace the Gemini SDK with any other LLM provider. + +--- + +## πŸ›  Prerequisites + +- Python 3.10+ +- [Dapr and Dapr Cli](https://docs.dapr.io/getting-started/install-dapr-cli/) +- Docker (for Redis state backend via `dapr init`) +- Gemini API Key (sign up at [Gemini](https://aistudio.google.com/app/apikey/)) + +Install dependencies: + +```bash +pip install -r requirements.txt +``` + +--- + +## πŸš€ Running the App + +### 1. Start the Dapr-enabled FastAPI server + +```bash +dapr run --app-id contract-review --app-port 8000 -- uvicorn app:app --reload +``` + +This launches the FastAPI app with Dapr Workflow runtime. You’ll see: + +``` +== Dapr Workflow Runtime started == +``` + +--- + +### 2. Submit a contract for review + +For subsequent runs, make sure to use a new `id`. + +```bash +curl -X POST http://localhost:8000/review \ + -H "Content-Type: application/json" \ + -d '{ + "id": "C100", + "text": "SERVICE AGREEMENT\n\nThis Service Agreement (\"Agreement\") is entered into between ACME Data Solutions, Inc. (\"Provider\") and ClientCo LLC (\"Client\") effective as of August 1, 2025.\n\n1. SERVICES\nProvider agrees to deliver cloud data analytics services to Client as described in Exhibit A.\n\n2. PAYMENT TERMS\nClient shall pay Provider $50,000 per month, due within 15 days of invoice date. Failure to pay within 30 days may result in suspension of services.\n\n3. DATA OWNERSHIP\nAll data processed shall become the property of Provider, including any derivative works, without limitation.\n\n4. LIABILITY LIMITATION\nProvider shall not be liable for any damages, including loss of revenue, indirect, incidental, or consequential damages, even if advised of the possibility thereof.\n\n5. TERMINATION\nEither party may terminate this Agreement at any time with 5 days’ written notice. Client shall remain responsible for payment for all services rendered up to the termination date.\n\n6. CONFIDENTIALITY\nBoth parties agree to maintain confidentiality of proprietary information for a period of 12 months following termination.\n\nIN WITNESS WHEREOF, the parties have executed this Agreement as of the date first written above." +}' +``` + +This creates a new durable workflow instance: `workflow-C100`. + +When starting future contract approval workflows, use a new workflow instance id for every call. + +--- + +### 3. (Optional) Approve the contract if required + +If Gemini flags the contract as high risk, the workflow pauses and waits for this: + +```bash +curl -X POST http://localhost:8000/approve/workflow-C100 -H "Content-Type: application/json" -d '{ + "approved": true, + "reviewer": "Alice", + "notes": "Reviewed and approved as-is." +}' +``` + +If approval is not received in 24 hours, the workflow times out and continues gracefully. + +--- + +## 🧠 Why Use Dapr Workflows? + +Traditional LLM apps forget things, fail silently, and can’t wait. + +Dapr Workflows gives your agent: + +- **Durability** β€” survives restarts and crashes +- **Event waiting** β€” pauses for human input or external triggers +- **Retries & orchestration** β€” handles timeouts, branches, parallel execution +- **Composability** β€” plug in tools, APIs, databases, and humans + +--- + +## πŸ§ͺ Example Output + +```json +{ + "id": "C100", + "analysis": { + "summary": "The contract includes broad data ownership and limited liability.", + "risk_score": 82, + "risky_clauses": ["3. Data Ownership", "4. Liability Limitation"] + }, + "approved": true, + "approver": "Alice", + "notes": "Reviewed and approved as-is." +} +``` + +--- + +## πŸ“š Resources + +- [🧭 Dapr Workflow Documentation](https://docs.dapr.io/developing-applications/building-blocks/workflow/workflow-overview/) \ No newline at end of file diff --git a/AI/agentic_workflows/gemini/app.py b/AI/agentic_workflows/gemini/app.py new file mode 100644 index 000000000..317fdd27f --- /dev/null +++ b/AI/agentic_workflows/gemini/app.py @@ -0,0 +1,157 @@ +from fastapi import FastAPI +from pydantic import BaseModel +from contextlib import asynccontextmanager +from dapr.ext.workflow.workflow_runtime import WorkflowRuntime +from dapr.ext.workflow import DaprWorkflowClient +from datetime import timedelta +import dapr.ext.workflow as wf + +import json, time +from dataclasses import dataclass +from google import genai + +wfr = WorkflowRuntime() + +# === Gemini Setup === +client = genai.Client() + +@dataclass +class Approval: + approved: bool + reviewer: str + notes: str + + @staticmethod + def from_dict(dict): + return Approval(**dict) + +def complete(prompt: str, json_required: bool = False): + if json_required: + response = client.models.generate_content( + model="gemini-2.5-flash", + contents=prompt, + config={ + "response_mime_type": "application/json", + }, + ) + + try: + return json.loads(response.text) + except: + return { + "summary": response.text, + "risk_score": 50, + "risky_clauses": [] + } + else: + response = client.models.generate_content( + model="gemini-2.5-flash", + contents=prompt, + ) + + return response.text + +def analyze_contract(ctx, contract): + prompt = f""" +You are a contract analysis assistant. +Summarize key clauses and identify potential risks. +You **must** return valid JSON: summary, risk_score (0-100), risky_clauses. +Contract: +{contract['text']} +""" + + return complete(prompt, json_required=True) + +def draft_amendment(ctx, clauses): + prompt = f"Draft legally sound amendments for:\n{clauses}" + return complete(prompt) + +def send_for_approval(ctx, amendment): + print(f"[Send] Amendment sent: {amendment}...", flush=True) + time.sleep(1) + return {"sent": True} + +def final_report(ctx, all_results): + return json.dumps(all_results, indent=2) + +# === Workflow === +def contract_review_wf(ctx: wf.DaprWorkflowContext, contracts: list): + results = [] + tasks = [ctx.call_activity(analyze_contract, input=contract) for contract in contracts] + analyses = yield wf.when_all(tasks) + + for contract, analysis in zip(contracts, analyses): + result = {"id": contract["id"], "analysis": analysis} + + if analysis["risk_score"] > 70: + amendment = yield ctx.call_activity(draft_amendment, input=analysis["risky_clauses"]) + yield ctx.call_activity(send_for_approval, input=amendment) + + print("Waiting on approval for contract " + contract['id'], flush=True) + approval_event = ctx.wait_for_external_event("contract_approval") + timer = ctx.create_timer(timedelta(hours=24)) + completed_task = yield wf.when_any([approval_event, timer]) + + if completed_task == approval_event: + event_result = Approval.from_dict(approval_event.get_result()) + result["approved"] = event_result.approved + result['approver'] = event_result.reviewer + result['notes'] = event_result.notes + else: + result["approved"] = False + + results.append(result) + + report = yield ctx.call_activity(final_report, input=results) + print(report, flush=True) + return report + +# === FastAPI Lifespan === +@asynccontextmanager +async def lifespan(app: FastAPI): + wfr.register_workflow(contract_review_wf) + wfr.register_activity(analyze_contract) + wfr.register_activity(draft_amendment) + wfr.register_activity(send_for_approval) + wfr.register_activity(final_report) + + wfr.start() + print("== Dapr Workflow Runtime started ==") + yield + wfr.shutdown() + print("== Dapr Workflow Runtime stopped ==") + +app = FastAPI(lifespan=lifespan) + +# === API === +class ContractInput(BaseModel): + id: str + text: str + +class ApprovalInput(BaseModel): + approved: bool + reviewer: str + notes: str + +@app.post("/review") +def start_contract_review(contract: ContractInput): + client = DaprWorkflowClient() + instance_id = f"workflow-{contract.id}" + workflow_input = [{"id": contract.id, "text": contract.text}] + + scheduled_id = client.schedule_new_workflow( + workflow=contract_review_wf, + input=workflow_input, + instance_id=instance_id + ) + return {"instance_id": scheduled_id, "status": "started"} + +@app.post("/approve/{instance_id}") +def approve_contract(instance_id: str, approval: ApprovalInput): + client = DaprWorkflowClient() + client.raise_workflow_event( + instance_id=instance_id, + event_name="contract_approval", + data=approval.model_dump() + ) + return {"status": "approval sent", "instance_id": instance_id} diff --git a/AI/agentic_workflows/gemini/requirements.txt b/AI/agentic_workflows/gemini/requirements.txt new file mode 100644 index 000000000..ad1c5f6b8 --- /dev/null +++ b/AI/agentic_workflows/gemini/requirements.txt @@ -0,0 +1,3 @@ +dapr-ext-workflow +google-genai +uvicorn \ No newline at end of file diff --git a/AI/agentic_workflows/openai/README.md b/AI/agentic_workflows/openai/README.md new file mode 100644 index 000000000..f0b6476bf --- /dev/null +++ b/AI/agentic_workflows/openai/README.md @@ -0,0 +1,121 @@ +# 🧠 Agentic Contract Review Workflow (Dapr + OpenAI + FastAPI) + +This project demonstrates how to build a **durable, agentic AI workflow** using [Dapr Workflows](https://docs.dapr.io/developing-applications/building-blocks/workflow/) and [OpenAI](https://openai.com/api/). It showcases how to mix the intelligence of a large language model with the resilience and statefulness of a distributed workflow engine. + +--- + +## ✨ What It Does + +OpenAI plays the role of a contract analyst, and Dapr Workflows orchestrates the process across time: + +1. OpenAI reviews each contract and identifies risky clauses. +2. If the contract is risky, OpenAI drafts amendments. +3. The workflow **waits for human approval** β€” with timeout and persistence. +4. The results are compiled into a final structured report. + +Everything runs in a clean FastAPI service using Dapr's durable execution engine underneath. + +Even though this example is using OpenAI, you can replace the OpenAI SDK with any other LLM provider. + +--- + +## πŸ›  Prerequisites + +- Python 3.10+ +- [Dapr and Dapr Cli](https://docs.dapr.io/getting-started/install-dapr-cli/) +- Docker (for Redis state backend via `dapr init`) +- OpenAI API Key (sign up at [OpenAI](https://openai.com/api/)) + +Install dependencies: + +```bash +pip install -r requirements.txt +``` + +--- + +## πŸš€ Running the App + +### 1. Start the Dapr-enabled FastAPI server + +```bash +dapr run --app-id contract-review --app-port 8000 -- uvicorn app:app --reload +``` + +This launches the FastAPI app with Dapr Workflow runtime. You’ll see: + +``` +== Dapr Workflow Runtime started == +``` + +--- + +### 2. Submit a contract for review + +For subsequent runs, make sure to use a new `id`. + +```bash +curl -X POST http://localhost:8000/review \ + -H "Content-Type: application/json" \ + -d '{ + "id": "C100", + "text": "SERVICE AGREEMENT\n\nThis Service Agreement (\"Agreement\") is entered into between ACME Data Solutions, Inc. (\"Provider\") and ClientCo LLC (\"Client\") effective as of August 1, 2025.\n\n1. SERVICES\nProvider agrees to deliver cloud data analytics services to Client as described in Exhibit A.\n\n2. PAYMENT TERMS\nClient shall pay Provider $50,000 per month, due within 15 days of invoice date. Failure to pay within 30 days may result in suspension of services.\n\n3. DATA OWNERSHIP\nAll data processed shall become the property of Provider, including any derivative works, without limitation.\n\n4. LIABILITY LIMITATION\nProvider shall not be liable for any damages, including loss of revenue, indirect, incidental, or consequential damages, even if advised of the possibility thereof.\n\n5. TERMINATION\nEither party may terminate this Agreement at any time with 5 days’ written notice. Client shall remain responsible for payment for all services rendered up to the termination date.\n\n6. CONFIDENTIALITY\nBoth parties agree to maintain confidentiality of proprietary information for a period of 12 months following termination.\n\nIN WITNESS WHEREOF, the parties have executed this Agreement as of the date first written above." +}' +``` + +This creates a new durable workflow instance: `workflow-C100`. + +When starting future contract approval workflows, use a new workflow instance id for every call. + +--- + +### 3. (Optional) Approve the contract if required + +If OpenAI flags the contract as high risk, the workflow pauses and waits for this: + +```bash +curl -X POST http://localhost:8000/approve/workflow-C100 -H "Content-Type: application/json" -d '{ + "approved": true, + "reviewer": "Alice", + "notes": "Reviewed and approved as-is." +}' +``` + +If approval is not received in 24 hours, the workflow times out and continues gracefully. + +--- + +## 🧠 Why Use Dapr Workflows? + +Traditional LLM apps forget things, fail silently, and can’t wait. + +Dapr Workflows gives your agent: + +- **Durability** β€” survives restarts and crashes +- **Event waiting** β€” pauses for human input or external triggers +- **Retries & orchestration** β€” handles timeouts, branches, parallel execution +- **Composability** β€” plug in tools, APIs, databases, and humans + +--- + +## πŸ§ͺ Example Output + +```json +{ + "id": "C100", + "analysis": { + "summary": "The contract includes broad data ownership and limited liability.", + "risk_score": 82, + "risky_clauses": ["3. Data Ownership", "4. Liability Limitation"] + }, + "approved": true, + "approver": "Alice", + "notes": "Reviewed and approved as-is." +} +``` + +--- + +## πŸ“š Resources + +- [🧭 Dapr Workflow Documentation](https://docs.dapr.io/developing-applications/building-blocks/workflow/workflow-overview/) \ No newline at end of file diff --git a/AI/agentic_workflows/openai/app.py b/AI/agentic_workflows/openai/app.py new file mode 100644 index 000000000..31c6122a0 --- /dev/null +++ b/AI/agentic_workflows/openai/app.py @@ -0,0 +1,157 @@ +from fastapi import FastAPI +from pydantic import BaseModel +from contextlib import asynccontextmanager +from dapr.ext.workflow.workflow_runtime import WorkflowRuntime +from dapr.ext.workflow import DaprWorkflowClient +from datetime import timedelta +import dapr.ext.workflow as wf + +import os, json, time +from dataclasses import dataclass +import openai + +wfr = WorkflowRuntime() + +# === OpenAI Setup === +OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") +openai_client = openai.OpenAI(api_key=OPENAI_API_KEY) + +@dataclass +class Approval: + approved: bool + reviewer: str + notes: str + + @staticmethod + def from_dict(dict): + return Approval(**dict) + +# === Activities === +def analyze_contract(ctx, contract): + prompt = f""" +You are a contract analysis assistant. +Summarize key clauses and identify potential risks. +You **must** return valid JSON: summary, risk_score (0-100), risky_clauses. +Contract: +{contract['text']} +""" + response = openai_client.chat.completions.create( + model="gpt-4o", + messages=[{"role": "user", "content": prompt}], + max_tokens=800, + temperature=0.3, + response_format={ "type": "json_object" } + ) + + response_text = response.choices[0].message.content + + try: + return json.loads(response_text) + except: + return { + "summary": response_text, + "risk_score": 50, + "risky_clauses": [] + } + +def draft_amendment(ctx, clauses): + prompt = f"Draft legally sound amendments for:\n{clauses}" + + response = openai_client.chat.completions.create( + model="gpt-4o", + messages=[{"role": "user", "content": prompt}], + max_tokens=800, + temperature=0.3, + ) + + return response.choices[0].message.content + +def send_for_approval(ctx, amendment): + print(f"[Send] Amendment sent: {amendment}...", flush=True) + time.sleep(1) + return {"sent": True} + +def final_report(ctx, all_results): + return json.dumps(all_results, indent=2) + +# === Workflow === +def contract_review_wf(ctx: wf.DaprWorkflowContext, contracts: list): + results = [] + tasks = [ctx.call_activity(analyze_contract, input=contract) for contract in contracts] + analyses = yield wf.when_all(tasks) + + for contract, analysis in zip(contracts, analyses): + result = {"id": contract["id"], "analysis": analysis} + + if analysis["risk_score"] > 70: + amendment = yield ctx.call_activity(draft_amendment, input=analysis["risky_clauses"]) + yield ctx.call_activity(send_for_approval, input=amendment) + + print("Waiting on approval for contract " + contract['id'], flush=True) + approval_event = ctx.wait_for_external_event("contract_approval") + timer = ctx.create_timer(timedelta(hours=24)) + completed_task = yield wf.when_any([approval_event, timer]) + + if completed_task == approval_event: + event_result = Approval.from_dict(approval_event.get_result()) + result["approved"] = event_result.approved + result['approver'] = event_result.reviewer + result['notes'] = event_result.notes + else: + result["approved"] = False + + results.append(result) + + report = yield ctx.call_activity(final_report, input=results) + print(report, flush=True) + return report + +# === FastAPI Lifespan === +@asynccontextmanager +async def lifespan(app: FastAPI): + wfr.register_workflow(contract_review_wf) + wfr.register_activity(analyze_contract) + wfr.register_activity(draft_amendment) + wfr.register_activity(send_for_approval) + wfr.register_activity(final_report) + + wfr.start() + print("== Dapr Workflow Runtime started ==") + yield + wfr.shutdown() + print("== Dapr Workflow Runtime stopped ==") + +app = FastAPI(lifespan=lifespan) + +# === API === +class ContractInput(BaseModel): + id: str + text: str + +class ApprovalInput(BaseModel): + approved: bool + reviewer: str + notes: str + +@app.post("/review") +def start_contract_review(contract: ContractInput): + client = DaprWorkflowClient() + instance_id = f"workflow-{contract.id}" + workflow_input = [{"id": contract.id, "text": contract.text}] + + scheduled_id = client.schedule_new_workflow( + workflow=contract_review_wf, + input=workflow_input, + instance_id=instance_id + ) + return {"instance_id": scheduled_id, "status": "started"} + +@app.post("/approve/{instance_id}") +def approve_contract(instance_id: str, approval: ApprovalInput): + client = DaprWorkflowClient() + client.raise_workflow_event( + instance_id=instance_id, + event_name="contract_approval", + data=approval.model_dump() + ) + return {"status": "approval sent", "instance_id": instance_id} diff --git a/AI/agentic_workflows/openai/requirements.txt b/AI/agentic_workflows/openai/requirements.txt new file mode 100644 index 000000000..8cc286cc9 --- /dev/null +++ b/AI/agentic_workflows/openai/requirements.txt @@ -0,0 +1,3 @@ +dapr-ext-workflow +openai +uvicorn \ No newline at end of file diff --git a/README.md b/README.md index b02493a0e..766bc143a 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,9 @@ Pick a building block API (for example, PubSub, state management, etc) and rapid | Dapr Quickstart | Description | |:--------:|:--------:| +| [Workflow](./workflows) | Dapr Workflow enables you to create long running, fault-tolerant, stateful applications | | [Publish and Subscribe](./pub_sub) | Asynchronous communication between two services using messaging | +| [AI](./AI) | Agentic building blocks including durable workflows, eventing, authentication and state persistence | | [Service Invocation](./service_invocation) | Synchronous communication between two services using HTTP | | [State Management](./state_management/) | Store a service's data as key/value pairs in supported state stores | | [Bindings](./bindings/) | Work with external systems using input bindings to respond to events and output bindings to call operations | @@ -25,7 +27,6 @@ Pick a building block API (for example, PubSub, state management, etc) and rapid | [Configuration](./configuration) | Get configuration items as key/value pairs or subscribe to changes whenever a configuration item changes | | [Cryptography](./cryptography) | Perform cryptographic operations without exposing keys to your application | | [Resiliency](./resiliency) | Define and apply fault-tolerant policies (retries/back-offs, timeouts and circuit breakers) to your Dapr API requests | -| [Workflow](./workflows) | Dapr Workflow enables you to create long running, fault-tolerant, stateful applications | | [Jobs](./jobs) | Dapr Jobs enable you to manage and schedule tasks | ### Tutorials From a8e53cdfa088f63e2110f282ef8181c3803bd841 Mon Sep 17 00:00:00 2001 From: yaron2 Date: Mon, 15 Sep 2025 13:00:53 -0700 Subject: [PATCH 2/6] change dir structure Signed-off-by: yaron2 --- .../human_in_the_loop}/.gitignore | 0 .../human_in_the_loop}/README.md | 3 +-- .../human_in_the_loop}/anthropic/README.md | 0 .../human_in_the_loop}/anthropic/app.py | 0 .../human_in_the_loop}/anthropic/requirements.txt | 0 .../human_in_the_loop}/gemini/README.md | 0 .../human_in_the_loop}/gemini/app.py | 0 .../human_in_the_loop}/gemini/requirements.txt | 0 .../human_in_the_loop}/openai/README.md | 0 .../human_in_the_loop}/openai/app.py | 0 .../human_in_the_loop}/openai/requirements.txt | 0 11 files changed, 1 insertion(+), 2 deletions(-) rename AI/{agentic_workflows => workflows/human_in_the_loop}/.gitignore (100%) rename AI/{agentic_workflows => workflows/human_in_the_loop}/README.md (97%) rename AI/{agentic_workflows => workflows/human_in_the_loop}/anthropic/README.md (100%) rename AI/{agentic_workflows => workflows/human_in_the_loop}/anthropic/app.py (100%) rename AI/{agentic_workflows => workflows/human_in_the_loop}/anthropic/requirements.txt (100%) rename AI/{agentic_workflows => workflows/human_in_the_loop}/gemini/README.md (100%) rename AI/{agentic_workflows => workflows/human_in_the_loop}/gemini/app.py (100%) rename AI/{agentic_workflows => workflows/human_in_the_loop}/gemini/requirements.txt (100%) rename AI/{agentic_workflows => workflows/human_in_the_loop}/openai/README.md (100%) rename AI/{agentic_workflows => workflows/human_in_the_loop}/openai/app.py (100%) rename AI/{agentic_workflows => workflows/human_in_the_loop}/openai/requirements.txt (100%) diff --git a/AI/agentic_workflows/.gitignore b/AI/workflows/human_in_the_loop/.gitignore similarity index 100% rename from AI/agentic_workflows/.gitignore rename to AI/workflows/human_in_the_loop/.gitignore diff --git a/AI/agentic_workflows/README.md b/AI/workflows/human_in_the_loop/README.md similarity index 97% rename from AI/agentic_workflows/README.md rename to AI/workflows/human_in_the_loop/README.md index 7375c51d4..56332d2e6 100644 --- a/AI/agentic_workflows/README.md +++ b/AI/workflows/human_in_the_loop/README.md @@ -1,4 +1,4 @@ -## 🧠 Agentic Contract Review β€” Durable Workflows with Dapr (+ OpenAI, Gemini, Anthropic) +## 🧠 Agentic Human-In-The-Loop β€” Durable Workflows with Dapr (+ OpenAI, Gemini, Anthropic) This repo demonstrates a durable, agentic AI workflow for contract review built on Dapr Workflows and a simple FastAPI service. It ships three interchangeable implementations using different LLM providers: @@ -59,7 +59,6 @@ curl -X POST http://localhost:8000/review -H "Content-Type: application/json" -d # 6) If required, approve later by posting to /approve/{instanceId} -```bash curl -X POST http://localhost:8000/approve/workflow- -H "Content-Type: application/json" -d '{ "approved": true, "reviewer": "Alice", diff --git a/AI/agentic_workflows/anthropic/README.md b/AI/workflows/human_in_the_loop/anthropic/README.md similarity index 100% rename from AI/agentic_workflows/anthropic/README.md rename to AI/workflows/human_in_the_loop/anthropic/README.md diff --git a/AI/agentic_workflows/anthropic/app.py b/AI/workflows/human_in_the_loop/anthropic/app.py similarity index 100% rename from AI/agentic_workflows/anthropic/app.py rename to AI/workflows/human_in_the_loop/anthropic/app.py diff --git a/AI/agentic_workflows/anthropic/requirements.txt b/AI/workflows/human_in_the_loop/anthropic/requirements.txt similarity index 100% rename from AI/agentic_workflows/anthropic/requirements.txt rename to AI/workflows/human_in_the_loop/anthropic/requirements.txt diff --git a/AI/agentic_workflows/gemini/README.md b/AI/workflows/human_in_the_loop/gemini/README.md similarity index 100% rename from AI/agentic_workflows/gemini/README.md rename to AI/workflows/human_in_the_loop/gemini/README.md diff --git a/AI/agentic_workflows/gemini/app.py b/AI/workflows/human_in_the_loop/gemini/app.py similarity index 100% rename from AI/agentic_workflows/gemini/app.py rename to AI/workflows/human_in_the_loop/gemini/app.py diff --git a/AI/agentic_workflows/gemini/requirements.txt b/AI/workflows/human_in_the_loop/gemini/requirements.txt similarity index 100% rename from AI/agentic_workflows/gemini/requirements.txt rename to AI/workflows/human_in_the_loop/gemini/requirements.txt diff --git a/AI/agentic_workflows/openai/README.md b/AI/workflows/human_in_the_loop/openai/README.md similarity index 100% rename from AI/agentic_workflows/openai/README.md rename to AI/workflows/human_in_the_loop/openai/README.md diff --git a/AI/agentic_workflows/openai/app.py b/AI/workflows/human_in_the_loop/openai/app.py similarity index 100% rename from AI/agentic_workflows/openai/app.py rename to AI/workflows/human_in_the_loop/openai/app.py diff --git a/AI/agentic_workflows/openai/requirements.txt b/AI/workflows/human_in_the_loop/openai/requirements.txt similarity index 100% rename from AI/agentic_workflows/openai/requirements.txt rename to AI/workflows/human_in_the_loop/openai/requirements.txt From 3972c07a584e35c8b729afbb7606cfa20782a3c9 Mon Sep 17 00:00:00 2001 From: yaron2 Date: Wed, 17 Sep 2025 07:13:13 -0700 Subject: [PATCH 3/6] change dir name Signed-off-by: yaron2 --- .../{human_in_the_loop => external_system_interaction}/.gitignore | 0 .../{human_in_the_loop => external_system_interaction}/README.md | 0 .../anthropic/README.md | 0 .../anthropic/app.py | 0 .../anthropic/requirements.txt | 0 .../gemini/README.md | 0 .../gemini/app.py | 0 .../gemini/requirements.txt | 0 .../openai/README.md | 0 .../openai/app.py | 0 .../openai/requirements.txt | 0 11 files changed, 0 insertions(+), 0 deletions(-) rename AI/workflows/{human_in_the_loop => external_system_interaction}/.gitignore (100%) rename AI/workflows/{human_in_the_loop => external_system_interaction}/README.md (100%) rename AI/workflows/{human_in_the_loop => external_system_interaction}/anthropic/README.md (100%) rename AI/workflows/{human_in_the_loop => external_system_interaction}/anthropic/app.py (100%) rename AI/workflows/{human_in_the_loop => external_system_interaction}/anthropic/requirements.txt (100%) rename AI/workflows/{human_in_the_loop => external_system_interaction}/gemini/README.md (100%) rename AI/workflows/{human_in_the_loop => external_system_interaction}/gemini/app.py (100%) rename AI/workflows/{human_in_the_loop => external_system_interaction}/gemini/requirements.txt (100%) rename AI/workflows/{human_in_the_loop => external_system_interaction}/openai/README.md (100%) rename AI/workflows/{human_in_the_loop => external_system_interaction}/openai/app.py (100%) rename AI/workflows/{human_in_the_loop => external_system_interaction}/openai/requirements.txt (100%) diff --git a/AI/workflows/human_in_the_loop/.gitignore b/AI/workflows/external_system_interaction/.gitignore similarity index 100% rename from AI/workflows/human_in_the_loop/.gitignore rename to AI/workflows/external_system_interaction/.gitignore diff --git a/AI/workflows/human_in_the_loop/README.md b/AI/workflows/external_system_interaction/README.md similarity index 100% rename from AI/workflows/human_in_the_loop/README.md rename to AI/workflows/external_system_interaction/README.md diff --git a/AI/workflows/human_in_the_loop/anthropic/README.md b/AI/workflows/external_system_interaction/anthropic/README.md similarity index 100% rename from AI/workflows/human_in_the_loop/anthropic/README.md rename to AI/workflows/external_system_interaction/anthropic/README.md diff --git a/AI/workflows/human_in_the_loop/anthropic/app.py b/AI/workflows/external_system_interaction/anthropic/app.py similarity index 100% rename from AI/workflows/human_in_the_loop/anthropic/app.py rename to AI/workflows/external_system_interaction/anthropic/app.py diff --git a/AI/workflows/human_in_the_loop/anthropic/requirements.txt b/AI/workflows/external_system_interaction/anthropic/requirements.txt similarity index 100% rename from AI/workflows/human_in_the_loop/anthropic/requirements.txt rename to AI/workflows/external_system_interaction/anthropic/requirements.txt diff --git a/AI/workflows/human_in_the_loop/gemini/README.md b/AI/workflows/external_system_interaction/gemini/README.md similarity index 100% rename from AI/workflows/human_in_the_loop/gemini/README.md rename to AI/workflows/external_system_interaction/gemini/README.md diff --git a/AI/workflows/human_in_the_loop/gemini/app.py b/AI/workflows/external_system_interaction/gemini/app.py similarity index 100% rename from AI/workflows/human_in_the_loop/gemini/app.py rename to AI/workflows/external_system_interaction/gemini/app.py diff --git a/AI/workflows/human_in_the_loop/gemini/requirements.txt b/AI/workflows/external_system_interaction/gemini/requirements.txt similarity index 100% rename from AI/workflows/human_in_the_loop/gemini/requirements.txt rename to AI/workflows/external_system_interaction/gemini/requirements.txt diff --git a/AI/workflows/human_in_the_loop/openai/README.md b/AI/workflows/external_system_interaction/openai/README.md similarity index 100% rename from AI/workflows/human_in_the_loop/openai/README.md rename to AI/workflows/external_system_interaction/openai/README.md diff --git a/AI/workflows/human_in_the_loop/openai/app.py b/AI/workflows/external_system_interaction/openai/app.py similarity index 100% rename from AI/workflows/human_in_the_loop/openai/app.py rename to AI/workflows/external_system_interaction/openai/app.py diff --git a/AI/workflows/human_in_the_loop/openai/requirements.txt b/AI/workflows/external_system_interaction/openai/requirements.txt similarity index 100% rename from AI/workflows/human_in_the_loop/openai/requirements.txt rename to AI/workflows/external_system_interaction/openai/requirements.txt From 16eb9d023eb7efd94c9e74e355df44850d50b8b3 Mon Sep 17 00:00:00 2001 From: yaron2 Date: Wed, 17 Sep 2025 08:43:39 -0700 Subject: [PATCH 4/6] update README Signed-off-by: yaron2 --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 766bc143a..ed86128f2 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ Pick a building block API (for example, PubSub, state management, etc) and rapid | Dapr Quickstart | Description | |:--------:|:--------:| | [Workflow](./workflows) | Dapr Workflow enables you to create long running, fault-tolerant, stateful applications | +| [AI](./AI) | Create AI agents and apps that are durable, fault-tolerant with built-in synchronous and asynchronous messaging | | [Publish and Subscribe](./pub_sub) | Asynchronous communication between two services using messaging | | [AI](./AI) | Agentic building blocks including durable workflows, eventing, authentication and state persistence | | [Service Invocation](./service_invocation) | Synchronous communication between two services using HTTP | From 2a9193edc229093c30ad48a3ecc7410fd54ea012 Mon Sep 17 00:00:00 2001 From: yaron2 Date: Wed, 17 Sep 2025 15:11:14 -0700 Subject: [PATCH 5/6] review feedback Signed-off-by: yaron2 --- AI/workflows/external_system_interaction/README.md | 4 ++-- AI/workflows/external_system_interaction/anthropic/README.md | 2 +- AI/workflows/external_system_interaction/gemini/README.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/AI/workflows/external_system_interaction/README.md b/AI/workflows/external_system_interaction/README.md index 56332d2e6..20abf3064 100644 --- a/AI/workflows/external_system_interaction/README.md +++ b/AI/workflows/external_system_interaction/README.md @@ -28,7 +28,7 @@ All three implementations share these semantics; you can swap providers without ### πŸ›  Prerequisites (All Providers) * Python 3.10+ -* [Dapr and Dapr Cli](https://docs.dapr.io/getting-started/install-dapr-cli/) +* [Dapr and Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli/) * Docker (for local Dapr dependencies) * Provider API key (one of): * **OpenAI**: OPENAI\_API\_KEY @@ -43,7 +43,7 @@ Pick a provider folder and follow its README. The flow is generally: ```bash # 1) Choose a provider implementation -cd gemini # or openai / anthropic +cd openai # or gemini / anthropic # 2) Install deps pip install -r requirements.txt diff --git a/AI/workflows/external_system_interaction/anthropic/README.md b/AI/workflows/external_system_interaction/anthropic/README.md index e68113330..52c0cd7b5 100644 --- a/AI/workflows/external_system_interaction/anthropic/README.md +++ b/AI/workflows/external_system_interaction/anthropic/README.md @@ -22,7 +22,7 @@ Even though this example is using Claude, you can replace the Anthropic SDK with ## πŸ›  Prerequisites - Python 3.10+ -- [Dapr and Dapr Cli](https://docs.dapr.io/getting-started/install-dapr-cli/) +- [Dapr and Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli/) - Docker (for Redis state backend via `dapr init`) - Anthropic Claude API Key (sign up at [Anthropic](https://www.anthropic.com/)) diff --git a/AI/workflows/external_system_interaction/gemini/README.md b/AI/workflows/external_system_interaction/gemini/README.md index 0d62752b3..7a0d5cdf2 100644 --- a/AI/workflows/external_system_interaction/gemini/README.md +++ b/AI/workflows/external_system_interaction/gemini/README.md @@ -22,7 +22,7 @@ Even though this example is using Gemini, you can replace the Gemini SDK with an ## πŸ›  Prerequisites - Python 3.10+ -- [Dapr and Dapr Cli](https://docs.dapr.io/getting-started/install-dapr-cli/) +- [Dapr and Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli/) - Docker (for Redis state backend via `dapr init`) - Gemini API Key (sign up at [Gemini](https://aistudio.google.com/app/apikey/)) From 9a636718ebcf002e95241b9c559dfc4dfdf359e0 Mon Sep 17 00:00:00 2001 From: yaron2 Date: Wed, 17 Sep 2025 15:11:40 -0700 Subject: [PATCH 6/6] review feedback Signed-off-by: yaron2 --- AI/workflows/external_system_interaction/openai/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AI/workflows/external_system_interaction/openai/README.md b/AI/workflows/external_system_interaction/openai/README.md index f0b6476bf..fa0b30c6d 100644 --- a/AI/workflows/external_system_interaction/openai/README.md +++ b/AI/workflows/external_system_interaction/openai/README.md @@ -22,7 +22,7 @@ Even though this example is using OpenAI, you can replace the OpenAI SDK with an ## πŸ›  Prerequisites - Python 3.10+ -- [Dapr and Dapr Cli](https://docs.dapr.io/getting-started/install-dapr-cli/) +- [Dapr and Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli/) - Docker (for Redis state backend via `dapr init`) - OpenAI API Key (sign up at [OpenAI](https://openai.com/api/))