@bluefly/openstandardagents 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1 -1
- package/README.md +58 -218
- package/dist/cli/commands/diff.command.d.ts +7 -0
- package/dist/cli/commands/diff.command.d.ts.map +1 -0
- package/dist/cli/commands/diff.command.js +181 -0
- package/dist/cli/commands/diff.command.js.map +1 -0
- package/dist/cli/commands/docs.command.d.ts +7 -0
- package/dist/cli/commands/docs.command.d.ts.map +1 -0
- package/dist/cli/commands/docs.command.js +274 -0
- package/dist/cli/commands/docs.command.js.map +1 -0
- package/dist/cli/commands/lint.command.d.ts +7 -0
- package/dist/cli/commands/lint.command.d.ts.map +1 -0
- package/dist/cli/commands/lint.command.js +342 -0
- package/dist/cli/commands/lint.command.js.map +1 -0
- package/dist/cli/commands/serve.command.d.ts +7 -0
- package/dist/cli/commands/serve.command.d.ts.map +1 -0
- package/dist/cli/commands/serve.command.js +232 -0
- package/dist/cli/commands/serve.command.js.map +1 -0
- package/dist/cli/index.js +4 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/di-container.d.ts.map +1 -1
- package/dist/di-container.js +3 -0
- package/dist/di-container.js.map +1 -1
- package/dist/services/git.service.d.ts +40 -0
- package/dist/services/git.service.d.ts.map +1 -0
- package/dist/services/git.service.js +122 -0
- package/dist/services/git.service.js.map +1 -0
- package/dist/spec/v0.3.1/UNIFIED-SCHEMA.md +120 -0
- package/dist/spec/v0.3.1/adapters/drupal.md +541 -0
- package/dist/spec/v0.3.1/adapters/symfony.md +659 -0
- package/dist/spec/v0.3.1/agent-test.schema.json +75 -0
- package/dist/spec/v0.3.1/examples/drupal-content-writer.ossa.yaml +110 -0
- package/dist/spec/v0.3.1/examples/drupal-moderation-assistant.ossa.yaml +96 -0
- package/dist/spec/v0.3.1/examples/quick-wins/complete-agent-with-quick-wins.ossa.yaml +144 -0
- package/dist/spec/v0.3.1/extensions/drupal.md +417 -0
- package/dist/spec/v0.3.1/ossa-0.3.0.schema.json +2787 -0
- package/dist/spec/v0.3.1/ossa-0.3.1.schema.json +2806 -0
- package/dist/spec/v0.3.1/protocols/sse.md +494 -0
- package/dist/spec/v0.3.1/protocols/webrtc.md +600 -0
- package/dist/spec/v0.3.1/protocols/websocket.md +362 -0
- package/dist/spec/v0.3.1/schemas/agent-unified.yaml +165 -0
- package/dist/spec/v0.3.1/schemas/capabilities.yaml +102 -0
- package/dist/spec/v0.3.1/schemas/functions.yaml +75 -0
- package/dist/spec/v0.3.1/schemas/messaging/channel.schema.json +245 -0
- package/dist/spec/v0.3.1/schemas/messaging/delivery-receipt.schema.json +192 -0
- package/dist/spec/v0.3.1/schemas/messaging/message.schema.json +205 -0
- package/dist/spec/v0.3.1/schemas/messaging/subscription.schema.json +214 -0
- package/dist/spec/v0.3.1/schemas/runtime.yaml +102 -0
- package/dist/spec/v0.3.1/schemas/taxonomy.yaml +533 -0
- package/dist/spec/v0.3.1/schemas/unified-llm.yaml +91 -0
- package/dist/spec/v0.3.1/taxonomy.yaml +256 -0
- package/dist/testing/fixtures.d.ts.map +1 -1
- package/dist/testing/fixtures.js +3 -2
- package/dist/testing/fixtures.js.map +1 -1
- package/package.json +3 -31
- package/spec/v0.3.1/UNIFIED-SCHEMA.md +120 -0
- package/spec/v0.3.1/adapters/drupal.md +541 -0
- package/spec/v0.3.1/adapters/symfony.md +659 -0
- package/spec/v0.3.1/agent-test.schema.json +75 -0
- package/spec/v0.3.1/examples/drupal-content-writer.ossa.yaml +110 -0
- package/spec/v0.3.1/examples/drupal-moderation-assistant.ossa.yaml +96 -0
- package/spec/v0.3.1/examples/quick-wins/complete-agent-with-quick-wins.ossa.yaml +144 -0
- package/spec/v0.3.1/extensions/drupal.md +417 -0
- package/spec/v0.3.1/ossa-0.3.0.schema.json +2787 -0
- package/spec/v0.3.1/ossa-0.3.1.schema.json +2806 -0
- package/spec/v0.3.1/protocols/sse.md +494 -0
- package/spec/v0.3.1/protocols/webrtc.md +600 -0
- package/spec/v0.3.1/protocols/websocket.md +362 -0
- package/spec/v0.3.1/schemas/agent-unified.yaml +165 -0
- package/spec/v0.3.1/schemas/capabilities.yaml +102 -0
- package/spec/v0.3.1/schemas/functions.yaml +75 -0
- package/spec/v0.3.1/schemas/messaging/channel.schema.json +245 -0
- package/spec/v0.3.1/schemas/messaging/delivery-receipt.schema.json +192 -0
- package/spec/v0.3.1/schemas/messaging/message.schema.json +205 -0
- package/spec/v0.3.1/schemas/messaging/subscription.schema.json +214 -0
- package/spec/v0.3.1/schemas/runtime.yaml +102 -0
- package/spec/v0.3.1/schemas/taxonomy.yaml +533 -0
- package/spec/v0.3.1/schemas/unified-llm.yaml +91 -0
- package/spec/v0.3.1/taxonomy.yaml +256 -0
- package/dist/types/generated/ossa-0.3.0.types.d.ts +0 -316
- package/dist/types/generated/ossa-0.3.0.types.d.ts.map +0 -1
- package/dist/types/generated/ossa-0.3.0.types.js +0 -8
- package/dist/types/generated/ossa-0.3.0.types.js.map +0 -1
- package/dist/types/generated/ossa-0.3.0.zod.d.ts +0 -17
- package/dist/types/generated/ossa-0.3.0.zod.d.ts.map +0 -1
- package/dist/types/generated/ossa-0.3.0.zod.js +0 -3
- package/dist/types/generated/ossa-0.3.0.zod.js.map +0 -1
|
@@ -0,0 +1,659 @@
|
|
|
1
|
+
# Symfony Messenger OSSA Runtime Adapter
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The Symfony Messenger OSSA Runtime Adapter enables execution of OSSA Task, Workflow, and Agent manifests using Symfony Messenger's asynchronous message queue system. This provides enterprise-grade async processing with support for multiple transports.
|
|
6
|
+
|
|
7
|
+
## Architecture
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
11
|
+
│ Symfony Application │
|
|
12
|
+
├─────────────────────────────────────────────────────────────────┤
|
|
13
|
+
│ ┌─────────────────┐ ┌─────────────────────────────────────┐ │
|
|
14
|
+
│ │ OSSA Manifest │ │ OSSA Message Handler │ │
|
|
15
|
+
│ │ (Task/Workflow)│───▶│ • OSSATaskMessageHandler │ │
|
|
16
|
+
│ └─────────────────┘ │ • OSSAWorkflowMessageHandler │ │
|
|
17
|
+
│ │ • OSSAAgentMessageHandler │ │
|
|
18
|
+
│ └──────────────┬──────────────────────┘ │
|
|
19
|
+
│ │ │
|
|
20
|
+
│ ┌──────────────▼──────────────────────┐ │
|
|
21
|
+
│ │ Symfony Messenger Bus │ │
|
|
22
|
+
│ └──────────────┬──────────────────────┘ │
|
|
23
|
+
│ │ │
|
|
24
|
+
│ ┌──────────────┬──────────────┬───────┴─────┬──────────────┐ │
|
|
25
|
+
│ │ AMQP │ Redis │ Doctrine │ SQS │ │
|
|
26
|
+
│ │ Transport │ Transport │ Transport │ Transport │ │
|
|
27
|
+
│ └──────┬───────┴──────┬───────┴──────┬──────┴──────┬───────┘ │
|
|
28
|
+
├─────────┼──────────────┼──────────────┼─────────────┼──────────┤
|
|
29
|
+
│ ┌──────▼───────┐ ┌────▼────┐ ┌───────▼──────┐ ┌────▼────┐ │
|
|
30
|
+
│ │ RabbitMQ │ │ Redis │ │ Database │ │ SQS │ │
|
|
31
|
+
│ └──────────────┘ └─────────┘ └──────────────┘ └─────────┘ │
|
|
32
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
33
|
+
│
|
|
34
|
+
┌─────────▼─────────┐
|
|
35
|
+
│ Messenger Worker │
|
|
36
|
+
│ messenger:consume│
|
|
37
|
+
└─────────┬─────────┘
|
|
38
|
+
│
|
|
39
|
+
┌─────────▼─────────┐
|
|
40
|
+
│ Task Execution │
|
|
41
|
+
└───────────────────┘
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Installation
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
composer require ossa/symfony-adapter
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Configuration
|
|
51
|
+
|
|
52
|
+
### Bundle Configuration (`config/packages/ossa.yaml`)
|
|
53
|
+
|
|
54
|
+
```yaml
|
|
55
|
+
ossa:
|
|
56
|
+
# Manifest discovery
|
|
57
|
+
manifest_paths:
|
|
58
|
+
- '%kernel.project_dir%/config/ossa'
|
|
59
|
+
- '%kernel.project_dir%/src/*/Resources/ossa'
|
|
60
|
+
|
|
61
|
+
# Default runtime settings
|
|
62
|
+
defaults:
|
|
63
|
+
timeout_seconds: 300
|
|
64
|
+
retry_attempts: 3
|
|
65
|
+
retry_delay_ms: 1000
|
|
66
|
+
|
|
67
|
+
# Observability
|
|
68
|
+
observability:
|
|
69
|
+
enabled: true
|
|
70
|
+
exporter: otel
|
|
71
|
+
endpoint: '%env(OTEL_EXPORTER_ENDPOINT)%'
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Messenger Configuration (`config/packages/messenger.yaml`)
|
|
75
|
+
|
|
76
|
+
```yaml
|
|
77
|
+
framework:
|
|
78
|
+
messenger:
|
|
79
|
+
transports:
|
|
80
|
+
# OSSA Task transport
|
|
81
|
+
ossa_tasks:
|
|
82
|
+
dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
|
|
83
|
+
options:
|
|
84
|
+
queue_name: ossa_tasks
|
|
85
|
+
retry_strategy:
|
|
86
|
+
max_retries: 3
|
|
87
|
+
delay: 1000
|
|
88
|
+
multiplier: 2
|
|
89
|
+
|
|
90
|
+
# OSSA Workflow transport
|
|
91
|
+
ossa_workflows:
|
|
92
|
+
dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
|
|
93
|
+
options:
|
|
94
|
+
queue_name: ossa_workflows
|
|
95
|
+
retry_strategy:
|
|
96
|
+
max_retries: 5
|
|
97
|
+
delay: 2000
|
|
98
|
+
|
|
99
|
+
# OSSA Agent transport (higher priority)
|
|
100
|
+
ossa_agents:
|
|
101
|
+
dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
|
|
102
|
+
options:
|
|
103
|
+
queue_name: ossa_agents
|
|
104
|
+
|
|
105
|
+
routing:
|
|
106
|
+
'OSSA\Messenger\Message\OSSATaskMessage': ossa_tasks
|
|
107
|
+
'OSSA\Messenger\Message\OSSAWorkflowMessage': ossa_workflows
|
|
108
|
+
'OSSA\Messenger\Message\OSSAAgentMessage': ossa_agents
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Message Types
|
|
112
|
+
|
|
113
|
+
### OSSATaskMessage
|
|
114
|
+
|
|
115
|
+
Wraps a `kind: Task` manifest for async execution:
|
|
116
|
+
|
|
117
|
+
```php
|
|
118
|
+
<?php
|
|
119
|
+
|
|
120
|
+
namespace OSSA\Messenger\Message;
|
|
121
|
+
|
|
122
|
+
final class OSSATaskMessage
|
|
123
|
+
{
|
|
124
|
+
public function __construct(
|
|
125
|
+
private readonly string $manifestPath,
|
|
126
|
+
private readonly array $input,
|
|
127
|
+
private readonly ?string $correlationId = null,
|
|
128
|
+
private readonly array $metadata = [],
|
|
129
|
+
) {}
|
|
130
|
+
|
|
131
|
+
public function getManifestPath(): string
|
|
132
|
+
{
|
|
133
|
+
return $this->manifestPath;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
public function getInput(): array
|
|
137
|
+
{
|
|
138
|
+
return $this->input;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
public function getCorrelationId(): ?string
|
|
142
|
+
{
|
|
143
|
+
return $this->correlationId;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
public function getMetadata(): array
|
|
147
|
+
{
|
|
148
|
+
return $this->metadata;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### OSSAWorkflowMessage
|
|
154
|
+
|
|
155
|
+
Wraps a `kind: Workflow` manifest with step orchestration:
|
|
156
|
+
|
|
157
|
+
```php
|
|
158
|
+
<?php
|
|
159
|
+
|
|
160
|
+
namespace OSSA\Messenger\Message;
|
|
161
|
+
|
|
162
|
+
final class OSSAWorkflowMessage
|
|
163
|
+
{
|
|
164
|
+
public function __construct(
|
|
165
|
+
private readonly string $manifestPath,
|
|
166
|
+
private readonly array $input,
|
|
167
|
+
private readonly ?string $workflowInstanceId = null,
|
|
168
|
+
private readonly ?string $parentStepId = null,
|
|
169
|
+
private readonly array $context = [],
|
|
170
|
+
) {}
|
|
171
|
+
|
|
172
|
+
// ... getters
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Message Handlers
|
|
177
|
+
|
|
178
|
+
### OSSATaskMessageHandler
|
|
179
|
+
|
|
180
|
+
```php
|
|
181
|
+
<?php
|
|
182
|
+
|
|
183
|
+
namespace OSSA\Messenger\Handler;
|
|
184
|
+
|
|
185
|
+
use OSSA\Messenger\Message\OSSATaskMessage;
|
|
186
|
+
use OSSA\Runtime\TaskExecutor;
|
|
187
|
+
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
|
|
188
|
+
|
|
189
|
+
#[AsMessageHandler]
|
|
190
|
+
final class OSSATaskMessageHandler
|
|
191
|
+
{
|
|
192
|
+
public function __construct(
|
|
193
|
+
private readonly TaskExecutor $executor,
|
|
194
|
+
private readonly ManifestLoader $manifestLoader,
|
|
195
|
+
) {}
|
|
196
|
+
|
|
197
|
+
public function __invoke(OSSATaskMessage $message): void
|
|
198
|
+
{
|
|
199
|
+
$manifest = $this->manifestLoader->load($message->getManifestPath());
|
|
200
|
+
|
|
201
|
+
$result = $this->executor->execute($manifest, $message->getInput());
|
|
202
|
+
|
|
203
|
+
// Result is stored/emitted based on manifest observability settings
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### OSSAWorkflowMessageHandler
|
|
209
|
+
|
|
210
|
+
```php
|
|
211
|
+
<?php
|
|
212
|
+
|
|
213
|
+
namespace OSSA\Messenger\Handler;
|
|
214
|
+
|
|
215
|
+
use OSSA\Messenger\Message\OSSAWorkflowMessage;
|
|
216
|
+
use OSSA\Messenger\Message\OSSATaskMessage;
|
|
217
|
+
use OSSA\Runtime\WorkflowOrchestrator;
|
|
218
|
+
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
|
|
219
|
+
use Symfony\Component\Messenger\MessageBusInterface;
|
|
220
|
+
|
|
221
|
+
#[AsMessageHandler]
|
|
222
|
+
final class OSSAWorkflowMessageHandler
|
|
223
|
+
{
|
|
224
|
+
public function __construct(
|
|
225
|
+
private readonly WorkflowOrchestrator $orchestrator,
|
|
226
|
+
private readonly MessageBusInterface $bus,
|
|
227
|
+
) {}
|
|
228
|
+
|
|
229
|
+
public function __invoke(OSSAWorkflowMessage $message): void
|
|
230
|
+
{
|
|
231
|
+
$workflow = $this->orchestrator->loadWorkflow($message->getManifestPath());
|
|
232
|
+
|
|
233
|
+
foreach ($workflow->getReadySteps($message->getContext()) as $step) {
|
|
234
|
+
if ($step->getKind() === 'Task') {
|
|
235
|
+
// Dispatch Task step as separate message
|
|
236
|
+
$this->bus->dispatch(new OSSATaskMessage(
|
|
237
|
+
$step->getRef(),
|
|
238
|
+
$step->getResolvedInput($message->getContext()),
|
|
239
|
+
correlationId: $message->getWorkflowInstanceId(),
|
|
240
|
+
));
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## Transport Support
|
|
248
|
+
|
|
249
|
+
### AMQP (RabbitMQ)
|
|
250
|
+
|
|
251
|
+
```yaml
|
|
252
|
+
# .env
|
|
253
|
+
MESSENGER_TRANSPORT_DSN=amqp://guest:guest@localhost:5672/%2f/messages
|
|
254
|
+
|
|
255
|
+
# config/packages/messenger.yaml
|
|
256
|
+
framework:
|
|
257
|
+
messenger:
|
|
258
|
+
transports:
|
|
259
|
+
ossa_tasks:
|
|
260
|
+
dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
|
|
261
|
+
options:
|
|
262
|
+
exchange:
|
|
263
|
+
name: ossa
|
|
264
|
+
type: topic
|
|
265
|
+
queues:
|
|
266
|
+
ossa_tasks:
|
|
267
|
+
binding_keys:
|
|
268
|
+
- 'task.*'
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Redis
|
|
272
|
+
|
|
273
|
+
```yaml
|
|
274
|
+
# .env
|
|
275
|
+
MESSENGER_TRANSPORT_DSN=redis://localhost:6379/messages
|
|
276
|
+
|
|
277
|
+
# config/packages/messenger.yaml
|
|
278
|
+
framework:
|
|
279
|
+
messenger:
|
|
280
|
+
transports:
|
|
281
|
+
ossa_tasks:
|
|
282
|
+
dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
|
|
283
|
+
options:
|
|
284
|
+
stream: ossa_tasks
|
|
285
|
+
group: ossa_workers
|
|
286
|
+
consumer: worker_%hostname%
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Doctrine
|
|
290
|
+
|
|
291
|
+
```yaml
|
|
292
|
+
# .env
|
|
293
|
+
MESSENGER_TRANSPORT_DSN=doctrine://default
|
|
294
|
+
|
|
295
|
+
# config/packages/messenger.yaml
|
|
296
|
+
framework:
|
|
297
|
+
messenger:
|
|
298
|
+
transports:
|
|
299
|
+
ossa_tasks:
|
|
300
|
+
dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
|
|
301
|
+
options:
|
|
302
|
+
table_name: ossa_messenger_messages
|
|
303
|
+
queue_name: ossa_tasks
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### Amazon SQS
|
|
307
|
+
|
|
308
|
+
```yaml
|
|
309
|
+
# .env
|
|
310
|
+
MESSENGER_TRANSPORT_DSN=sqs://default?region=us-east-1
|
|
311
|
+
|
|
312
|
+
# config/packages/messenger.yaml
|
|
313
|
+
framework:
|
|
314
|
+
messenger:
|
|
315
|
+
transports:
|
|
316
|
+
ossa_tasks:
|
|
317
|
+
dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
|
|
318
|
+
options:
|
|
319
|
+
queue_name: ossa-tasks
|
|
320
|
+
wait_time: 20
|
|
321
|
+
poll_timeout: 0.1
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
## Runtime Bindings
|
|
325
|
+
|
|
326
|
+
Map OSSA capabilities to Symfony services:
|
|
327
|
+
|
|
328
|
+
```yaml
|
|
329
|
+
# config/packages/ossa.yaml
|
|
330
|
+
ossa:
|
|
331
|
+
bindings:
|
|
332
|
+
send_email:
|
|
333
|
+
service: 'Symfony\Component\Mailer\MailerInterface'
|
|
334
|
+
method: send
|
|
335
|
+
|
|
336
|
+
http_request:
|
|
337
|
+
service: 'Symfony\Contracts\HttpClient\HttpClientInterface'
|
|
338
|
+
method: request
|
|
339
|
+
|
|
340
|
+
cache_get:
|
|
341
|
+
service: 'Symfony\Contracts\Cache\CacheInterface'
|
|
342
|
+
method: get
|
|
343
|
+
|
|
344
|
+
dispatch_event:
|
|
345
|
+
service: 'Symfony\Contracts\EventDispatcher\EventDispatcherInterface'
|
|
346
|
+
method: dispatch
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
## Example Task Manifest
|
|
350
|
+
|
|
351
|
+
```yaml
|
|
352
|
+
# config/ossa/tasks/send-email.yaml
|
|
353
|
+
apiVersion: ossa/v0.3.1
|
|
354
|
+
kind: Task
|
|
355
|
+
metadata:
|
|
356
|
+
name: send-email-symfony
|
|
357
|
+
version: 1.0.0
|
|
358
|
+
labels:
|
|
359
|
+
runtime: symfony
|
|
360
|
+
transport: messenger
|
|
361
|
+
|
|
362
|
+
spec:
|
|
363
|
+
execution:
|
|
364
|
+
type: deterministic
|
|
365
|
+
runtime: symfony
|
|
366
|
+
entrypoint: 'App\TaskHandler\SendEmailHandler::execute'
|
|
367
|
+
timeout_seconds: 30
|
|
368
|
+
|
|
369
|
+
capabilities:
|
|
370
|
+
- send_email
|
|
371
|
+
- render_template
|
|
372
|
+
|
|
373
|
+
input:
|
|
374
|
+
type: object
|
|
375
|
+
properties:
|
|
376
|
+
to:
|
|
377
|
+
type: string
|
|
378
|
+
format: email
|
|
379
|
+
subject:
|
|
380
|
+
type: string
|
|
381
|
+
template:
|
|
382
|
+
type: string
|
|
383
|
+
variables:
|
|
384
|
+
type: object
|
|
385
|
+
required:
|
|
386
|
+
- to
|
|
387
|
+
- subject
|
|
388
|
+
- template
|
|
389
|
+
|
|
390
|
+
output:
|
|
391
|
+
type: object
|
|
392
|
+
properties:
|
|
393
|
+
message_id:
|
|
394
|
+
type: string
|
|
395
|
+
sent_at:
|
|
396
|
+
type: string
|
|
397
|
+
format: date-time
|
|
398
|
+
|
|
399
|
+
error_handling:
|
|
400
|
+
on_error: retry
|
|
401
|
+
retry:
|
|
402
|
+
max_attempts: 3
|
|
403
|
+
backoff_strategy: exponential
|
|
404
|
+
|
|
405
|
+
runtime:
|
|
406
|
+
type: symfony
|
|
407
|
+
bindings:
|
|
408
|
+
send_email:
|
|
409
|
+
service: '@Symfony\Component\Mailer\MailerInterface'
|
|
410
|
+
render_template:
|
|
411
|
+
service: '@Twig\Environment'
|
|
412
|
+
|
|
413
|
+
messenger:
|
|
414
|
+
transport: ossa_tasks
|
|
415
|
+
routing_key: email.send
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
## Example Workflow Manifest
|
|
419
|
+
|
|
420
|
+
```yaml
|
|
421
|
+
# config/ossa/workflows/user-onboarding.yaml
|
|
422
|
+
apiVersion: ossa/v0.3.1
|
|
423
|
+
kind: Workflow
|
|
424
|
+
metadata:
|
|
425
|
+
name: user-onboarding-symfony
|
|
426
|
+
version: 1.0.0
|
|
427
|
+
|
|
428
|
+
spec:
|
|
429
|
+
triggers:
|
|
430
|
+
- type: event
|
|
431
|
+
source: symfony.event_dispatcher
|
|
432
|
+
event: user.registered
|
|
433
|
+
|
|
434
|
+
inputs:
|
|
435
|
+
type: object
|
|
436
|
+
properties:
|
|
437
|
+
user_id:
|
|
438
|
+
type: string
|
|
439
|
+
email:
|
|
440
|
+
type: string
|
|
441
|
+
required:
|
|
442
|
+
- user_id
|
|
443
|
+
- email
|
|
444
|
+
|
|
445
|
+
steps:
|
|
446
|
+
- id: validate_email
|
|
447
|
+
kind: Task
|
|
448
|
+
ref: ./tasks/validate-email.yaml
|
|
449
|
+
input:
|
|
450
|
+
email: '${{ workflow.input.email }}'
|
|
451
|
+
|
|
452
|
+
- id: create_profile
|
|
453
|
+
kind: Task
|
|
454
|
+
ref: ./tasks/create-profile.yaml
|
|
455
|
+
input:
|
|
456
|
+
user_id: '${{ workflow.input.user_id }}'
|
|
457
|
+
depends_on:
|
|
458
|
+
- validate_email
|
|
459
|
+
|
|
460
|
+
- id: send_welcome_email
|
|
461
|
+
kind: Task
|
|
462
|
+
ref: ./tasks/send-email.yaml
|
|
463
|
+
input:
|
|
464
|
+
to: '${{ workflow.input.email }}'
|
|
465
|
+
template: welcome
|
|
466
|
+
depends_on:
|
|
467
|
+
- create_profile
|
|
468
|
+
|
|
469
|
+
runtime:
|
|
470
|
+
type: symfony
|
|
471
|
+
messenger:
|
|
472
|
+
transport: ossa_workflows
|
|
473
|
+
step_dispatch:
|
|
474
|
+
transport: ossa_tasks
|
|
475
|
+
parallel_execution: true
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
## Worker Deployment
|
|
479
|
+
|
|
480
|
+
### Docker
|
|
481
|
+
|
|
482
|
+
```dockerfile
|
|
483
|
+
FROM php:8.2-cli
|
|
484
|
+
|
|
485
|
+
# Install dependencies
|
|
486
|
+
RUN docker-php-ext-install pdo_mysql pcntl
|
|
487
|
+
|
|
488
|
+
# Copy application
|
|
489
|
+
COPY . /app
|
|
490
|
+
WORKDIR /app
|
|
491
|
+
|
|
492
|
+
# Run worker
|
|
493
|
+
CMD ["php", "bin/console", "messenger:consume", "ossa_tasks", "ossa_workflows", "-vv"]
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
### Kubernetes
|
|
497
|
+
|
|
498
|
+
```yaml
|
|
499
|
+
apiVersion: apps/v1
|
|
500
|
+
kind: Deployment
|
|
501
|
+
metadata:
|
|
502
|
+
name: ossa-worker
|
|
503
|
+
spec:
|
|
504
|
+
replicas: 3
|
|
505
|
+
selector:
|
|
506
|
+
matchLabels:
|
|
507
|
+
app: ossa-worker
|
|
508
|
+
template:
|
|
509
|
+
spec:
|
|
510
|
+
containers:
|
|
511
|
+
- name: worker
|
|
512
|
+
image: myapp:latest
|
|
513
|
+
command: ["php", "bin/console", "messenger:consume"]
|
|
514
|
+
args:
|
|
515
|
+
- "ossa_tasks"
|
|
516
|
+
- "ossa_workflows"
|
|
517
|
+
- "--limit=100"
|
|
518
|
+
- "--time-limit=3600"
|
|
519
|
+
resources:
|
|
520
|
+
requests:
|
|
521
|
+
memory: "256Mi"
|
|
522
|
+
cpu: "250m"
|
|
523
|
+
limits:
|
|
524
|
+
memory: "512Mi"
|
|
525
|
+
cpu: "500m"
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
## Observability
|
|
529
|
+
|
|
530
|
+
### OpenTelemetry Integration
|
|
531
|
+
|
|
532
|
+
```php
|
|
533
|
+
<?php
|
|
534
|
+
// Automatic span creation for message handling
|
|
535
|
+
|
|
536
|
+
use OpenTelemetry\API\Trace\TracerInterface;
|
|
537
|
+
|
|
538
|
+
#[AsMessageHandler]
|
|
539
|
+
final class OSSATaskMessageHandler
|
|
540
|
+
{
|
|
541
|
+
public function __construct(
|
|
542
|
+
private readonly TracerInterface $tracer,
|
|
543
|
+
) {}
|
|
544
|
+
|
|
545
|
+
public function __invoke(OSSATaskMessage $message): void
|
|
546
|
+
{
|
|
547
|
+
$span = $this->tracer->spanBuilder('ossa.task.execute')
|
|
548
|
+
->setAttribute('ossa.task.name', $message->getManifestPath())
|
|
549
|
+
->setAttribute('ossa.correlation_id', $message->getCorrelationId())
|
|
550
|
+
->startSpan();
|
|
551
|
+
|
|
552
|
+
try {
|
|
553
|
+
// Execute task
|
|
554
|
+
} finally {
|
|
555
|
+
$span->end();
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
### Prometheus Metrics
|
|
562
|
+
|
|
563
|
+
```yaml
|
|
564
|
+
# Exposed at /metrics
|
|
565
|
+
ossa_task_messages_total{task="send-email", status="success"} 1234
|
|
566
|
+
ossa_task_duration_seconds{task="send-email"} 0.45
|
|
567
|
+
ossa_workflow_steps_completed{workflow="user-onboarding"} 567
|
|
568
|
+
ossa_messenger_queue_size{queue="ossa_tasks"} 12
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
## Error Handling
|
|
572
|
+
|
|
573
|
+
### Dead Letter Queue
|
|
574
|
+
|
|
575
|
+
```yaml
|
|
576
|
+
framework:
|
|
577
|
+
messenger:
|
|
578
|
+
transports:
|
|
579
|
+
ossa_tasks:
|
|
580
|
+
dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
|
|
581
|
+
failure_transport: ossa_failed
|
|
582
|
+
|
|
583
|
+
ossa_failed:
|
|
584
|
+
dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
|
|
585
|
+
options:
|
|
586
|
+
queue_name: ossa_failed
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
### Custom Error Handler
|
|
590
|
+
|
|
591
|
+
```php
|
|
592
|
+
<?php
|
|
593
|
+
|
|
594
|
+
namespace App\EventSubscriber;
|
|
595
|
+
|
|
596
|
+
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
|
597
|
+
use Symfony\Component\Messenger\Event\WorkerMessageFailedEvent;
|
|
598
|
+
|
|
599
|
+
class OSSAFailureSubscriber implements EventSubscriberInterface
|
|
600
|
+
{
|
|
601
|
+
public static function getSubscribedEvents(): array
|
|
602
|
+
{
|
|
603
|
+
return [
|
|
604
|
+
WorkerMessageFailedEvent::class => 'onMessageFailed',
|
|
605
|
+
];
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
public function onMessageFailed(WorkerMessageFailedEvent $event): void
|
|
609
|
+
{
|
|
610
|
+
$message = $event->getEnvelope()->getMessage();
|
|
611
|
+
|
|
612
|
+
if ($message instanceof OSSATaskMessage) {
|
|
613
|
+
// Log, alert, or handle failure
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
## CLI Commands
|
|
620
|
+
|
|
621
|
+
```bash
|
|
622
|
+
# Validate OSSA manifest
|
|
623
|
+
php bin/console ossa:validate config/ossa/tasks/my-task.yaml
|
|
624
|
+
|
|
625
|
+
# Dispatch a task
|
|
626
|
+
php bin/console ossa:dispatch task my-task --input='{"key": "value"}'
|
|
627
|
+
|
|
628
|
+
# Start a workflow
|
|
629
|
+
php bin/console ossa:dispatch workflow user-onboarding --input='{"user_id": "123"}'
|
|
630
|
+
|
|
631
|
+
# List pending messages
|
|
632
|
+
php bin/console messenger:stats
|
|
633
|
+
|
|
634
|
+
# Consume messages
|
|
635
|
+
php bin/console messenger:consume ossa_tasks ossa_workflows -vv
|
|
636
|
+
|
|
637
|
+
# Retry failed messages
|
|
638
|
+
php bin/console messenger:failed:retry --all
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
## OSSA Compliance
|
|
642
|
+
|
|
643
|
+
This adapter implements OSSA v0.3.1 specification:
|
|
644
|
+
|
|
645
|
+
- ✅ Task execution with deterministic semantics
|
|
646
|
+
- ✅ Workflow orchestration with step dependencies
|
|
647
|
+
- ✅ Async execution via Symfony Messenger
|
|
648
|
+
- ✅ Multiple transport support (AMQP, Redis, Doctrine, SQS)
|
|
649
|
+
- ✅ Capability abstraction and runtime bindings
|
|
650
|
+
- ✅ OpenTelemetry observability
|
|
651
|
+
- ✅ Error handling with retry strategies
|
|
652
|
+
- ✅ Dead letter queue support
|
|
653
|
+
|
|
654
|
+
## Related
|
|
655
|
+
|
|
656
|
+
- [OSSA Specification v0.3.1](../README.md)
|
|
657
|
+
- [Drupal Adapter](./drupal.md)
|
|
658
|
+
- [Node.js Adapter](./nodejs.md)
|
|
659
|
+
- [Capability Registry](../capability-schema.md)
|