Skip to content

Commit ec163e8

Browse files
authored
fix(general): use dulwich, basic dockerfile, config improvements (#87)
2 parents eb99fc3 + 6bbd4dd commit ec163e8

File tree

15 files changed

+167
-76
lines changed

15 files changed

+167
-76
lines changed

.dockerignore

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Envs
2+
.venv
3+
.venv*/
4+
.env
5+
.env.*
6+
.ruff_cache
7+
.pytest_cache
8+
.vscode
9+
.DS_Store
10+
11+
# Python-generated files
12+
__pycache__/
13+
*.py[oc]
14+
build/
15+
dist/
16+
wheels/
17+
*.egg-info
18+
19+
# Other folders
20+
docs/
21+
examples/
22+
tests/

Dockerfile

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Add <builder-digest> in prod
2+
FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim AS builder
3+
ENV UV_COMPILE_BYTECODE=1 \
4+
UV_LINK_MODE=copy \
5+
UV_PYTHON_DOWNLOADS=0
6+
WORKDIR /app
7+
RUN --mount=type=cache,target=/root/.cache/uv \
8+
--mount=type=bind,source=uv.lock,target=uv.lock \
9+
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
10+
uv sync --locked --no-install-project --all-extras --no-dev --no-editable
11+
COPY . /app
12+
RUN --mount=type=cache,target=/root/.cache/uv \
13+
uv sync --locked --all-extras --no-dev --no-editable
14+
RUN rm -rf /root/.cache/uv /root/.cache/pip
15+
16+
# Add <runtime-digest> in prod
17+
FROM python:3.12-slim-bookworm AS runtime
18+
19+
ENV PIP_NO_CACHE_DIR=1 \
20+
UV_PYTHON_DOWNLOADS=0
21+
RUN groupadd -r app && \
22+
useradd -r -g app -d /nonexistent -s /usr/sbin/nologin app
23+
USER app
24+
WORKDIR /app
25+
COPY --from=builder --chown=app:app /app /app
26+
ENV PATH="/app/.venv/bin:$PATH"
27+
CMD ["/app/.venv/bin/flare-ai-kit"]

examples/a2a/a2a_collaboration/ftso_agent.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ async def handle_send_message(request_body: SendMessageRequest) -> SendMessageRe
147147
if part.kind == "text":
148148
user_message += part.text
149149

150-
kit = FlareAIKit()
150+
kit = FlareAIKit(config=None) # Use default settings
151151
deps = AgentDependencies(flare_kit=kit)
152152

153153
result = await price_agent.run(user_message, deps=deps)

examples/a2a/a2a_collaboration/orchestrator_agent.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import asyncio
22
import os
3+
from pathlib import Path
34
from uuid import uuid4
45

56
import structlog
@@ -16,6 +17,7 @@
1617
SendMessageRequest,
1718
TextPart,
1819
)
20+
from flare_ai_kit.a2a.settings import A2ASettings
1921

2022
load_dotenv()
2123

@@ -184,7 +186,8 @@ async def route_workflow(
184186

185187
async def main() -> None:
186188
"""Async entrypoint for orchestrator agent."""
187-
client = A2AClient(db_path="orchestrator-agent-client.db")
189+
settings = A2ASettings(sqlite_db_path=Path("orchestrator-agent.db"))
190+
client = A2AClient(settings=settings)
188191

189192
await client.discover(
190193
[

examples/a2a/messaging/ping_pong.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import asyncio
2+
from pathlib import Path
23

34
from flare_ai_kit.a2a import A2AClient
45
from flare_ai_kit.a2a.schemas import (
@@ -7,6 +8,7 @@
78
SendMessageRequest,
89
TextPart,
910
)
11+
from flare_ai_kit.a2a.settings import A2ASettings
1012

1113

1214
async def main() -> None:
@@ -15,7 +17,8 @@ async def main() -> None:
1517
"https://system-integration.telex.im/ping_pong_agent/.well-known/agent.json"
1618
)
1719
agent_base_url = agent_card_url.split(".well-known")[0]
18-
client = A2AClient(db_path="tasks.db")
20+
settings = A2ASettings(sqlite_db_path=Path("tasks.db"))
21+
client = A2AClient(settings=settings)
1922

2023
message = SendMessageRequest(
2124
params=MessageSendParams(

pyproject.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,12 @@ Releases = "https://github.com/flare-foundation/flare-ai-kit/releases"
4242
Issues = "https://github.com/flare-foundation/flare-ai-kit/issues"
4343

4444
[project.scripts]
45-
flare-ai-kit = "flare_ai_kit:main"
45+
flare-ai-kit = "flare_ai_kit.main:start"
4646

4747
[project.optional-dependencies]
4848
rag = [
4949
"qdrant-client>=1.13.3",
50-
"gitpython>=3.1.44",
50+
"dulwich>=0.23.2",
5151
]
5252
social = [
5353
"python-telegram-bot>=22.0",
@@ -100,4 +100,4 @@ typeCheckingMode = "strict"
100100

101101
[tool.pytest.ini_options]
102102
asyncio_mode = "strict"
103-
asyncio_default_fixture_loop_scope = "function"
103+
asyncio_default_fixture_loop_scope = "function"

src/flare_ai_kit/a2a/client.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
from flare_ai_kit.a2a.task_management import TaskManager
1818
from flare_ai_kit.common import A2AClientError
1919

20+
from .settings import A2ASettings
21+
2022
if TYPE_CHECKING:
2123
from collections.abc import Coroutine
2224

@@ -31,16 +33,17 @@ class A2AClient:
3133
3. performing task management,
3234
"""
3335

34-
def __init__(self, db_path: str = ".", http_client_timeout: float = 30.0) -> None:
36+
def __init__(self, settings: A2ASettings) -> None:
3537
"""Initialize the A2A client with SQLite database for task tracking."""
36-
self.db_path = db_path
37-
self.task_manager = TaskManager(db_path)
38+
self.sqlite_db_path = settings.sqlite_db_path
39+
self.client_timeout = settings.client_timeout
40+
self.task_manager = TaskManager(self.sqlite_db_path)
3841
self.agent_cards: dict[str, AgentCard] = {}
3942
self.available_skills: list[AgentSkill] = []
4043
self.skill_to_agents: dict[
4144
str, list[str]
4245
] = {} # skill name -> list of agent URLs
43-
self.http_client = httpx.AsyncClient(timeout=http_client_timeout)
46+
self.http_client = httpx.AsyncClient(timeout=self.client_timeout)
4447

4548
async def send_message(
4649
self,

src/flare_ai_kit/a2a/settings.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
"""Settings for A2A."""
2+
3+
from pathlib import Path
4+
5+
from pydantic import Field
6+
from pydantic_settings import BaseSettings, SettingsConfigDict
7+
8+
9+
class A2ASettings(BaseSettings):
10+
"""Configuration specific to the Flare ecosystem interactions."""
11+
12+
model_config = SettingsConfigDict(
13+
env_prefix="A2A__",
14+
env_file=".env",
15+
extra="ignore",
16+
)
17+
sqlite_db_path: Path = Field(
18+
default=Path.cwd() / "flare_a2a.db",
19+
description="Use a pregenerated attestation token for testing.",
20+
)
21+
client_timeout: float = Field(
22+
default=30.0,
23+
description="Timeout for HTTP requests to A2A servers.",
24+
)

src/flare_ai_kit/a2a/task_management.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import sqlite3
99
import uuid
10+
from pathlib import Path
1011
from typing import ClassVar
1112

1213
from flare_ai_kit.a2a.schemas import Task, TaskState, TaskStatus
@@ -23,7 +24,7 @@ class TaskManager:
2324
TaskState.unknown,
2425
]
2526

26-
def __init__(self, db_path: str = "tasks.db") -> None:
27+
def __init__(self, db_path: Path = Path("flare_a2a.db")) -> None:
2728
"""Init method for the task manager."""
2829
self.db_path = db_path
2930
self.connection = sqlite3.connect(db_path)

src/flare_ai_kit/config.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from pydantic import Field
66
from pydantic_settings import BaseSettings, SettingsConfigDict
77

8+
from flare_ai_kit.a2a.settings import A2ASettings
89
from flare_ai_kit.agent.settings import AgentSettings
910
from flare_ai_kit.ecosystem.settings import EcosystemSettings
1011
from flare_ai_kit.ingestion.settings import IngestionSettings
@@ -36,3 +37,4 @@ class AppSettings(BaseSettings):
3637
social: SocialSettings = Field(default_factory=SocialSettings) # pyright: ignore[reportArgumentType,reportUnknownVariableType]
3738
tee: TeeSettings = Field(default_factory=TeeSettings) # pyright: ignore[reportArgumentType,reportUnknownVariableType]
3839
ingestion: IngestionSettings = Field(default_factory=IngestionSettings) # pyright: ignore[reportArgumentType,reportUnknownVariableType]
40+
a2a: A2ASettings = Field(default_factory=A2ASettings) # pyright: ignore[reportArgumentType,reportUnknownVariableType]

0 commit comments

Comments
 (0)