Skip to content

Commit cdcf8ef

Browse files
committed
added a2a cf deployment
1 parent cb6ecbb commit cdcf8ef

10 files changed

Lines changed: 147 additions & 50 deletions

File tree

exercises/Python/01-setup-dev-space.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
77
## Open SAP Business Application Studio
88

9-
👉 Go back to the [BTP cockpit](https://emea.cockpit.btp.cloud.sap/cockpit#/globalaccount/275320f9-4c26-4622-8728-b6f5196075f5/subaccount/a5a420d8-58c6-4820-ab11-90c7145da589/subaccountoverview).
9+
👉 Go back to the [BTP cockpit](https://emea.cockpit.btp.cloud.sap/cockpit/?idp=a7rg4vxjp.accounts.ondemand.com#/globalaccount/275320f9-4c26-4622-8728-b6f5196075f5/subaccount/a5a420d8-58c6-4820-ab11-90c7145da589?layout=TwoColumnsMidExpanded).
1010

1111
👉 Navigate to `Instances and Subscriptions` and open `SAP Business Application Studio`.
1212

@@ -56,7 +56,7 @@ https://github.com/SAP-samples/codejam-code-based-agents.git
5656

5757
```Python
5858
LITELLM_PROVIDER="sap"
59-
AICORE_AUTH_URL="https://#####.authentication.eu10.hana.ondemand.com/oauth/token"
59+
AICORE_AUTH_URL="https://#####.authentication.eu10.hana.ondemand.com/" + "oauth/token"
6060
AICORE_CLIENT_ID="sb-3c636fc2-d352-496a-851d-7a7d6005dcd4!b505946|aicore!b540"
6161
AICORE_CLIENT_SECRET="#####"
6262
AICORE_RESOURCE_GROUP="ai-agents-codejam"
@@ -65,7 +65,7 @@ RPT1_DEPLOYMENT_URL="https://api.ai.prod.eu-central-1.aws.ml.hana.ondemand.com/v
6565
```
6666
👉 You will need to UPDATE these variables but keep the given structure. The correct information you can find in the SAP AI Core service-key in your BTP cockpit.
6767

68-
👉 Go back to the Subaccount in the [BTP cockpit](https://emea.cockpit.btp.cloud.sap/cockpit#/globalaccount/275320f9-4c26-4622-8728-b6f5196075f5/subaccount/a5a420d8-58c6-4820-ab11-90c7145da589/subaccountoverview).
68+
👉 Go back to the Subaccount in the [BTP cockpit](https://emea.cockpit.btp.cloud.sap/cockpit/?idp=a7rg4vxjp.accounts.ondemand.com#/globalaccount/275320f9-4c26-4622-8728-b6f5196075f5/subaccount/a5a420d8-58c6-4820-ab11-90c7145da589?layout=TwoColumnsMidExpanded).
6969

7070
👉 Navigate to `Instances and Subscriptions` and open the SAP AI Core instance's service binding.
7171

exercises/Python/02-build-a-basic-agent.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ if __name__ == "__main__":
111111

112112
👉 Execute the crew with the basic agent:
113113

114-
> ☝️ Make sure you're in the repository root directory (e.g., `codejam-code-based-agents-1`) when running this command. If you're already in the `starter-project` folder, use the appropriate command for your OS.
114+
> ☝️ Make sure you're in the repository root directory (e.g., `codejam-code-based-agents`) when running this command. If you're already in the `starter-project` folder, use the appropriate command for your OS.
115115
116116
**From repository root:**
117117

project/Python/solution/.cfignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.venv/
2+
__pycache__/
3+
*.pyc
4+
*.pyo
5+
.env
6+
.git/
7+
exercises/
8+
assets/
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.13.11

project/Python/solution/config/agents.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ evidence_analyst_agent:
1313
Criminal Evidence Analyst
1414
goal: >
1515
Retrieve and analyze evidence ONLY via the call_grounding_service tool.
16-
Search for each suspect by name: {suspect_names}. Do NOT fabricate any evidence or alibis.
16+
Search for each suspect by name: Sophie Dubois, Marcus Chen, Viktor Petrov. Do NOT fabricate any evidence or alibis.
1717
Report only what the tool returns.
1818
backstory: >
1919
You are a methodical evidence analyst who bases conclusions strictly on retrieved documents. You never assume facts.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
applications:
2+
- name: investigator-crew-a2a
3+
memory: 1024M
4+
disk_quota: 2048M
5+
instances: 1
6+
buildpacks:
7+
- https://github.com/cloudfoundry/python-buildpack/releases/download/v1.8.43/python-buildpack-cflinuxfs4-v1.8.43.zip
8+
health-check-type: http
9+
health-check-http-endpoint: /health
10+
timeout: 180
11+
command: python -m uvicorn server:app --host 0.0.0.0 --port $PORT --workers 1
12+
services:
13+
- generative-ai-hub
14+
env:
15+
APP_URL: https://developer-advocates-free-tier-genai-codejam-luyq1wkg-dev9296031.cfapps.eu10-004.hana.ondemand.com
16+
LITELLM_PROVIDER: sap
17+
AICORE_RESOURCE_GROUP: ai-agents-codejam
18+
RPT1_DEPLOYMENT_URL: https://api.ai.prod.eu-central-1.aws.ml.hana.ondemand.com/v2/inference/deployments/d67ab78ae72fd41d/predict
19+
CREWAI_TRACING_ENABLED: "false"
20+
BP_PYTHON_VERSION: "3.13.11"
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Core CrewAI agent framework with A2A protocol support
2+
crewai[a2a]
3+
# A2A SDK with HTTP server support
4+
a2a-sdk[http-server]
5+
# LLM interaction with SAP Generative AI Hub
6+
litellm==1.82.6
7+
# Environment configuration
8+
python-dotenv
9+
# Data validation
10+
pydantic
11+
# HTTP client
12+
httpx
13+
# HTTP requests
14+
requests
15+
# YAML configuration files
16+
PyYAML
17+
# SAP AI Core SDK for integration with SAP Generative AI Hub
18+
sap-ai-sdk-base==3.4.0
19+
sap-ai-sdk-core==3.3.0
20+
sap-ai-sdk-gen==6.7.0

project/Python/solution/rpt_client.py

Lines changed: 0 additions & 45 deletions
This file was deleted.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
python-3.13.x

project/Python/solution/server.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import asyncio
2+
import json
3+
import os
4+
5+
from a2a.server.agent_execution import AgentExecutor, RequestContext
6+
from a2a.server.apps.jsonrpc import A2AFastAPIApplication
7+
from a2a.server.events import EventQueue
8+
from a2a.server.request_handlers import DefaultRequestHandler
9+
from a2a.server.tasks import InMemoryTaskStore
10+
from a2a.types import Artifact, TaskState, TaskStatus, TaskStatusUpdateEvent, TaskArtifactUpdateEvent, TextPart, AgentCard, AgentCapabilities, AgentSkill
11+
from fastapi.middleware.cors import CORSMiddleware
12+
13+
from investigator_crew import InvestigatorCrew
14+
from payload import payload
15+
16+
17+
class InvestigatorExecutor(AgentExecutor):
18+
async def execute(self, context: RequestContext, event_queue: EventQueue) -> None:
19+
await event_queue.enqueue_event(
20+
TaskStatusUpdateEvent(task_id=context.task_id, context_id=context.context_id, status=TaskStatus(state=TaskState.working), final=False)
21+
)
22+
user_input = context.get_user_input()
23+
try:
24+
parsed = json.loads(user_input)
25+
user_request = parsed.get("user_request", user_input)
26+
suspect_names = parsed.get("suspect_names", user_input)
27+
except (json.JSONDecodeError, TypeError):
28+
user_request = user_input
29+
suspect_names = user_input
30+
31+
loop = asyncio.get_event_loop()
32+
result = await loop.run_in_executor(
33+
None,
34+
lambda: InvestigatorCrew().crew().kickoff(
35+
inputs={"payload": payload, "user_request": user_request, "suspect_names": suspect_names}
36+
),
37+
)
38+
await event_queue.enqueue_event(
39+
TaskArtifactUpdateEvent(
40+
task_id=context.task_id,
41+
context_id=context.context_id,
42+
artifact=Artifact(artifactId="investigation_result", parts=[TextPart(text=str(result))], name="investigation_result"),
43+
)
44+
)
45+
await event_queue.enqueue_event(
46+
TaskStatusUpdateEvent(task_id=context.task_id, context_id=context.context_id, status=TaskStatus(state=TaskState.completed), final=True)
47+
)
48+
49+
async def cancel(self, context: RequestContext, event_queue: EventQueue) -> None:
50+
await event_queue.enqueue_event(
51+
TaskStatusUpdateEvent(task_id=context.task_id, context_id=context.context_id, status=TaskStatus(state=TaskState.canceled), final=True)
52+
)
53+
54+
55+
agent_card = AgentCard(
56+
name="Investigator Crew",
57+
description="Multi-agent art theft investigation crew exposed as an A2A server",
58+
url=os.environ.get("APP_URL", "http://localhost:8080"),
59+
version="1.0.0",
60+
capabilities=AgentCapabilities(streaming=False),
61+
skills=[
62+
AgentSkill(
63+
id="investigate",
64+
name="Investigate Art Theft",
65+
description="Investigates art theft cases by appraising losses and analyzing evidence",
66+
tags=["investigation", "art", "insurance", "theft"],
67+
inputModes=["text/plain"],
68+
outputModes=["text/markdown"],
69+
)
70+
],
71+
defaultInputModes=["text/plain"],
72+
defaultOutputModes=["text/markdown"],
73+
)
74+
handler = DefaultRequestHandler(agent_executor=InvestigatorExecutor(), task_store=InMemoryTaskStore())
75+
app = A2AFastAPIApplication(agent_card=agent_card, http_handler=handler).build()
76+
77+
app.add_middleware(
78+
CORSMiddleware,
79+
allow_origins=["*"],
80+
allow_methods=["*"],
81+
allow_headers=["*"],
82+
)
83+
84+
85+
@app.get("/health")
86+
def health():
87+
return {"status": "ok"}
88+
89+
90+
if __name__ == "__main__":
91+
import uvicorn
92+
uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT", 8080)))

0 commit comments

Comments
 (0)