Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .bumpversion.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# https://peps.python.org/pep-0440/

[tool.bumpversion]
current_version = "0.3.4"
current_version = "0.4.2"
parse = """(?x)
(?P<major>0|[1-9]\\d*)\\.
(?P<minor>0|[1-9]\\d*)\\.
Expand Down
129 changes: 129 additions & 0 deletions .env.exemple
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
#########################################
# Server Settings for Archetype Module
#########################################

# ══ Channel Settings ════════════════════════════════════════════════════════════════ #

# Host address on which the module gRPC server listens.
# Default: [::] (all IPv6 & IPv4 interfaces)
SERVER_CHANNEL_HOST=[::]

# TCP port for the module gRPC server.
# Default: 50055
SERVER_CHANNEL_PORT=50055

# Execution mode of the server. Possible values: async | sync
# Default: async
SERVER_CHANNEL_CONTROL_FLOW=async

# Security mode of the server. Possible values: insecure | secure
# Default: insecure
SERVER_CHANNEL_SECURITY=insecure

# Enable mutual TLS for incoming clients: "true" or "false"
# Default: false
SERVER_CHANNEL_MTLS=false

#SERVER_CHANNEL_CREDENTIALS__KEY_PATH=server.key
#SERVER_CHANNEL_CREDENTIALS__CERT_PATH=cert.key
#SERVER_CHANNEL_CREDENTIALS__ROOT_CERT_PATH=ca.crt

# Hostname or IP address that the server advertises to clients for connection.
# Default: digitalkin-ada-archetype
SERVER_CHANNEL_ADVERTISE_HOST=

# ══ gRPC Settings ════════════════════════════════════════════════════════════════════ #

# Compression algorithm for gRPC messages. Possible values: gzip, deflate, snappy, zstd, or none
# Default: gzip
SERVER_GRPC_COMPRESSION=gzip

# ── Option Grpc ───────────────────────────────────────────────────────────────────── #
# Time (in milliseconds) after which a keepalive ping is sent if the connection is idle.
# Default: 120000 (2 minutes)
SERVER_GRPC_OPTIONS_KEEPALIVE_TIME=120000

# Time (in milliseconds) the server waits for a keepalive ping ack before closing the connection.
# Default: 20000 (20 seconds)
SERVER_GRPC_OPTIONS_KEEPALIVE_TIMEOUT=20000

# Minimum time (in milliseconds) between client pings.
# Default: 10000 (10 seconds)
SERVER_GRPC_OPTIONS_MIN_PING_INTERVAL=10000

# Maximum message size (in bytes) the server can receive.
# Default: 4194304 (4MB)
SERVER_GRPC_OPTIONS_MAX_RECEIVE_MESSAGE_LENGTH=4194304

# Maximum message size (in bytes) the server can send.
# Default: 4194304 (4MB)
SERVER_GRPC_OPTIONS_MAX_SEND_MESSAGE_LENGTH=4194304

# Maximum number of pings the server allows without receiving any data from the client.
# Default: 0 (unlimited)
SERVER_GRPC_OPTIONS_MAX_PINGS_WITHOUT_DATA=0

# Allow keepalive pings when there are no active calls. "true" or "false"
# Default: true
SERVER_GRPC_OPTIONS_KEEPALIVE_PERMIT_WITHOUT_CALLS=true

#########################################
# gRPC Client Configuration Services Provider
#########################################

# Host address on which the services provider gRPC server listens.
# Default: [::] (all IPv6 & IPv4 interfaces)
SERVICES_PROVIDER_URL=[::]

# TCP port for the services provider gRPC server.
# Default: 50151
SERVICES_PROVIDER_PORT=50151

# Execution mode of the services provider. Possible values: async | sync
# Default: async
SERVICES_PROVIDER_MODE=async

# Security mode of the services provider. Possible values: insecure | secure
# Default: insecure
SERVICES_PROVIDER_SECURITY=insecure

# Enable mutual TLS for outgoing calls: "true" or "false"
# Default: false
SERVICES_PROVIDER_MTLS=false

GRPC_DNS_RESOLVER="native"

#############################################
# Other Configuration
#############################################

MODULE_ID_TOOLKIT_RAG="modules:1"

OPENAI_API_KEY=sk-xxxx

########################################
# Certificate Settings (CERTIFICATE_)
########################################

# Directory where server/client certificates are stored
# (default: "/certificates" / local: "./certs")
CERTIFICATE_CERT_VOLUME=/certificates

# Directory where registry CA certificates are stored
# (default: "/certificates" / local: "./certs")
CERTIFICATE_REGISTRY_CERT_VOLUME=/certificates

# (Optional) Directory for services_provider client.key, client.crt, and ca.crt
# If unset, defaults to CERTIFICATE_CERT_VOLUME
CERTIFICATE_SERVICES_PROVIDER_CERT_VOLUME=/certificates

########################################
# Langfuse Tracing Configuration
########################################

# Langfuse credentials for OTEL tracing
# Note: Authentication is handled programmatically using Base64-encoded Basic auth
# (public_key:secret_key), not via OTEL_EXPORTER_OTLP_HEADERS environment variable.
LANGFUSE_SECRET_KEY=sk-lf-xxxx
LANGFUSE_PUBLIC_KEY=pk-lf-xxxx
LANGFUSE_BASE_URL=https://langfuse.staging.digitalkin.com
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -183,10 +183,12 @@ cython_debug/
# IDE
.vscode/
.idea/
.DS_Store

# Project
requirements.txt
certs/
.report.json
docker-compose.override.yml

CLAUDE.md
6 changes: 3 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ repos:
- id: check-toml

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.14.14
rev: v0.15.11
hooks:
- id: ruff-check
args: [--fix]
- id: ruff-format

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.19.1
rev: v1.20.2
hooks:
- id: mypy
additional_dependencies: [types-protobuf]
Expand All @@ -34,7 +34,7 @@ repos:
hooks:
- id: pytest
name: pytest
entry: docker compose run --rm -T tests -m 'not integration'
entry: docker compose run --rm -T tests pytest tests/ -m 'not integration'
language: system
pass_filenames: false
types: [python]
Expand Down
49 changes: 49 additions & 0 deletions docs/changelog/0.3.4_to_0.3.5.dev0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
## Migration config serveur: `server_config` retire

A partir de `0.3.5.dev0`, la creation/configuration du serveur ne passe plus par `server_config` au moment de creer un module.
Les settings serveur sont lus directement via `ServerSettings` integres dans `src/digitalkin/grpc_servers/_base_server.py`.

## Variables changees (renommees)

| Ancienne variable | Nouvelle variable | Valeur par defaut |
|---|---|---|
| `MODULE_SERVER_HOST` | `SERVER_CHANNEL_HOST` | `[::]` |
| `MODULE_SERVER_MODE` | `SERVER_CHANNEL_CONTROL_FLOW` | `async` |
| `MODULE_SERVER_SECURITY` | `SERVER_CHANNEL_SECURITY` | `insecure` |
| `MODULE_SERVER_MTLS` | `SERVER_CHANNEL_MTLS` | `false` |
| `MODULE_SERVER_ADVERTISE_HOST` | `SERVER_CHANNEL_ADVERTISE_HOST` | `null` (non defini) |

## Variables ajoutees

| Variable | Valeur par defaut |
|---|---|
| `SERVER_CHANNEL_PORT` | `50055` |
| `SERVER_GRPC_COMPRESSION` | `gzip` |
| `SERVER_GRPC_OPTIONS_KEEPALIVE_TIME` | `120000` |
| `SERVER_GRPC_OPTIONS_KEEPALIVE_TIMEOUT` | `20000` |
| `SERVER_GRPC_OPTIONS_MIN_PING_INTERVAL` | `10000` |
| `SERVER_GRPC_OPTIONS_MAX_RECEIVE_MESSAGE_LENGTH` | `4194304` |
| `SERVER_GRPC_OPTIONS_MAX_SEND_MESSAGE_LENGTH` | `4194304` |
| `SERVER_GRPC_OPTIONS_MAX_PINGS_WITHOUT_DATA` | `0` |
| `SERVER_GRPC_OPTIONS_KEEPALIVE_PERMIT_WITHOUT_CALLS` | `true` |

## Exemple `.env` minimal (nouveau format)

```env
SERVER_CHANNEL_HOST=[::]
SERVER_CHANNEL_PORT=50055
SERVER_CHANNEL_CONTROL_FLOW=async
SERVER_CHANNEL_SECURITY=insecure
SERVER_CHANNEL_MTLS=false
# Optionnel
# SERVER_CHANNEL_ADVERTISE_HOST=archetype-ada-new.railway.internal

SERVER_GRPC_COMPRESSION=gzip
SERVER_GRPC_OPTIONS_KEEPALIVE_TIME=120000
SERVER_GRPC_OPTIONS_KEEPALIVE_TIMEOUT=20000
SERVER_GRPC_OPTIONS_MIN_PING_INTERVAL=10000
SERVER_GRPC_OPTIONS_MAX_RECEIVE_MESSAGE_LENGTH=4194304
SERVER_GRPC_OPTIONS_MAX_SEND_MESSAGE_LENGTH=4194304
SERVER_GRPC_OPTIONS_MAX_PINGS_WITHOUT_DATA=0
SERVER_GRPC_OPTIONS_KEEPALIVE_PERMIT_WITHOUT_CALLS=true
```
139 changes: 139 additions & 0 deletions docs/changelog/0.3.5_to_0.4.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
## Migration AG-UI : nouveau protocole de streaming

A partir de `0.4.0`, le SDK adopte le protocole **AG-UI** pour le streaming des evenements agent.
Les anciens mixins `UserMessageMixin` (callback_mixin) et `ChatHistoryMixin` sont **deprecated** et seront supprimes dans une version future.

## Nouveaux composants

### `AgUiMixin`

Mixin integre dans `BaseMixin` (donc disponible dans tous les `TriggerHandler`).
Convertit les evenements agent DigitalKin en evenements AG-UI et les envoie via `context.callbacks`.

```python
from digitalkin.mixins import AgUiMixin

class MyTrigger(TriggerHandler, AgUiMixin):
async def handle(self, input_data, setup_data, context):
for event in events:
await self.send_message(context, event)
```

Evenements supportes :
- **Run lifecycle** : `RunStarted`, `RunCompleted`, `RunError`
- **Text messages** : `TextMessageStart`, `TextMessageContent`, `TextMessageEnd`
- **Reasoning** : `ReasoningStart`, `ReasoningMessageContent`, `ReasoningEnd`
- **Tool calls** : `ToolCallStart`, `ToolCallArgs`, `ToolCallEnd`, `ToolCallResult`

### `AgnoStreamAdapter` (community)

Adaptateur pour convertir les evenements Agno en evenements DigitalKin.
Gere automatiquement le cycle de vie (ouverture/fermeture des sequences text/reasoning).

```python
from digitalkin.community.agno import AgnoStreamAdapter

adapter = AgnoStreamAdapter()

async for raw_event in agent.arun(message, stream=True, stream_events=True):
for event in adapter.to_digitalkin_events(raw_event):
await self.send_message(context, event)

# Fermer les sequences encore ouvertes en fin de stream
for event in adapter.flush():
await self.send_message(context, event)
```

### Modeles d'evenements (`digitalkin.models.events`)

Evenements framework-agnostic pour les runs agent :

| Classe | Description |
|---|---|
| `RunStartedEvent` | Debut d'un run agent |
| `RunContentEvent` | Contenu texte streame |
| `RunCompletedEvent` | Fin d'un run |
| `RunErrorEvent` | Erreur pendant le run |
| `TextMessageStartedEvent` | Debut d'un message texte |
| `TextMessageCompletedEvent` | Fin d'un message texte |
| `ReasoningStartedEvent` | Debut d'une phase de raisonnement |
| `ReasoningContentDeltaEvent` | Delta de contenu raisonnement |
| `ReasoningStepEvent` | Etape de raisonnement |
| `ReasoningCompletedEvent` | Fin de la phase de raisonnement |
| `ToolCallStartedEvent` | Debut d'un appel outil |
| `ToolCallCompletedEvent` | Fin d'un appel outil |
| `ToolCallErrorEvent` | Erreur d'un appel outil |

### Modeles de sortie AG-UI (`digitalkin.models.module.ag_ui`)

40+ types de sortie AG-UI avec serialisation camelCase automatique et union discriminee via le champ `protocol`.

## Architecture

```
Framework (Agno, ...)
|
v
AgnoStreamAdapter -- convertit les events framework -> DigitalKin
|
v
DigitalKin Events -- modeles framework-agnostic (agent_events.py)
|
v
AgUiMixin.send_message() -- convertit DigitalKin events -> AG-UI protocol
|
v
context.callbacks -- envoie vers le client via gRPC stream
```

## Exemple complet (Agno)

```python
from digitalkin.community.agno import AgnoStreamAdapter
from digitalkin.models.events import BaseAgentRunEvent
from digitalkin.modules.trigger_handler import TriggerHandler


@MyModule.register
class MessageTrigger(TriggerHandler):

protocol = "agui_stream"
input_format = AgUiStreamInput
output_format = AgUiEventOutput

async def handle(self, input_data, setup_data, context):
message = extract_user_message(input_data)

adapter = AgnoStreamAdapter()

async def send_event(event: BaseAgentRunEvent) -> None:
await self.send_message(context, event)

async for raw_event in agent.arun(message, stream=True, stream_events=True):
for event in adapter.to_digitalkin_events(raw_event):
await send_event(event)

for event in adapter.flush():
await send_event(event)
```

## Deprecations

| Ancien | Nouveau | Notes |
|---|---|---|
| `UserMessageMixin` | `AgUiMixin` | Emettra un `DeprecationWarning` a l'heritage |
| `ChatHistoryMixin` | `AgUiMixin` | Emettra un `DeprecationWarning` a l'heritage |
| `context.callbacks.send_message(output)` directement | `self.send_message(context, event)` via `AgUiMixin` | Les events passent maintenant par le pipeline AG-UI |

## `BaseMixin` mis a jour

```python
# Avant (0.3.5)
class BaseMixin(CostMixin, ChatHistoryMixin, FileHistoryMixin, LoggerMixin): ...

# Apres (0.4.0)
class BaseMixin(CostMixin, AgUiMixin, FileHistoryMixin, LoggerMixin): ...
```

`AgUiMixin` remplace `ChatHistoryMixin` dans la chaine d'heritage de `BaseMixin`.
Tous les `TriggerHandler` ont donc acces a `self.send_message(context, event)` sans import supplementaire.
Loading
Loading