@polymorphism-tech/morph-spec 8.38.0 → 8.41.0
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/CLAUDE.md +20 -9
- package/README.md +2 -2
- package/bin/morph-spec.js +60 -43
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +4 -3
- package/framework/MORPH.md +10 -9
- package/framework/agents.json +43 -26
- package/framework/docs/CLI.md +7 -5
- package/framework/evals/rubrics/code-quality.md +4 -4
- package/framework/evals/rubrics/condensed.md +2 -2
- package/framework/hooks/README.md +1 -1
- package/framework/hooks/claude-code/core/telemetry-log.js +39 -8
- package/framework/hooks/claude-code/core/worktree-create.js +22 -6
- package/framework/hooks/shared/git-paths.js +38 -5
- package/framework/mcp/registry.json +58 -66
- package/framework/rules/infrastructure-standards.md +0 -1
- package/framework/rules/morph-workflow.md +9 -4
- package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
- package/framework/skills/core/morph-apply/SKILL.md +59 -25
- package/framework/skills/core/morph-brainstorming/SKILL.md +1 -1
- package/framework/skills/core/morph-eval/SKILL.md +1 -1
- package/framework/skills/core/morph-hotfix/SKILL.md +2 -2
- package/framework/skills/core/morph-implement/SKILL.md +48 -24
- package/framework/skills/core/morph-media/SKILL.md +2 -2
- package/framework/skills/core/morph-plan/SKILL.md +10 -3
- package/framework/skills/core/morph-proposal/SKILL.md +1 -1
- package/framework/skills/core/morph-review/SKILL.md +1 -1
- package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
- package/framework/skills/core/morph-standards/SKILL.md +1 -1
- package/framework/skills/core/morph-uiux/SKILL.md +7 -6
- package/framework/standards/PROVENANCE.json +4 -4
- package/framework/standards/STANDARDS.json +9 -9
- package/framework/standards/ai-agents/context-providers.md +16 -26
- package/framework/standards/ai-agents/durable-workflows-hitl.md +15 -21
- package/framework/standards/ai-agents/modalities-image-gen.md +21 -25
- package/framework/standards/ai-agents/modalities-speech-to-text.md +4 -15
- package/framework/standards/ai-agents/modalities-text-to-speech.md +2 -11
- package/framework/standards/ai-agents/modalities-vision.md +1 -18
- package/framework/standards/ai-agents/prompt-sources.md +0 -4
- package/framework/standards/architecture/code-comments.md +52 -16
- package/framework/standards/backend/dotnet/testing.md +4 -2
- package/framework/standards/backend/integrations/neon-auth/neon-auth.md +63 -50
- package/framework/standards/frontend/design-system/ai-image-generation.md +42 -28
- package/framework/standards/frontend/design-system/ai-video-generation.md +24 -9
- package/framework/standards/frontend/nextjs/testing.md +4 -2
- package/framework/standards/integration/mcp/mcp-tools.md +64 -126
- package/framework/templates/code/dotnet/abstractions/result.cs +0 -2
- package/framework/templates/code/dotnet/ai-agents/AgentBootstrap.cs.template +26 -39
- package/framework/templates/code/dotnet/ai-agents/EmbeddingService.cs.template +28 -68
- package/framework/templates/code/dotnet/ai-agents/ModelRegistry.cs.template +99 -117
- package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +2 -44
- package/framework/templates/code/dotnet/ai-agents/ResilientToolMiddleware.cs.template +40 -61
- package/framework/templates/code/dotnet/ai-agents/VectorSearchTool.cs.template +54 -107
- package/framework/templates/code/dotnet/ai-agents/model-registry.json.template +53 -58
- package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +192 -281
- package/framework/templates/code/dotnet/database/migration.cs +49 -83
- package/framework/templates/code/dotnet/database/vector-entity.cs +2 -46
- package/framework/templates/code/dotnet/extensions/result-extensions.cs +0 -2
- package/framework/templates/code/dotnet/feature/validator.cs.hbs +11 -15
- package/framework/templates/code/dotnet/jobs/agent.cs +3 -51
- package/framework/templates/code/dotnet/jobs/job.cs +2 -97
- package/framework/templates/code/dotnet/test.cs +2 -87
- package/framework/templates/code/typescript/contracts.ts +140 -167
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentApi.cs +1 -48
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactory.cs +3 -79
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactoryOptions.cs +1 -70
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpec.cs +1 -47
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpecOptions.cs +2 -99
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/AiKitAssemblyMarker.cs +0 -6
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/ConversationStore.cs +3 -88
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/IConversationStore.cs +1 -41
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/InMemoryConversationStore.cs +0 -21
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/CountingChatClient.cs +1 -25
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalAssertion.cs +2 -63
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheMissException.cs +0 -30
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheOptions.cs +1 -52
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheStamp.cs +3 -78
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalFixture.cs +4 -109
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalMode.cs +0 -22
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalQualityEvaluators.cs +1 -36
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalScenario.cs +2 -57
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalSummary.cs +0 -75
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/OfflineChatClient.cs +2 -59
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/ShortPathResponseCache.cs +4 -111
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaGenerator.cs +0 -34
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaProvider.cs +1 -57
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaArtifact.cs +0 -19
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaDisclosure.cs +2 -74
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaGenerator.cs +2 -33
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaIdempotencyKey.cs +1 -43
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJob.cs +1 -96
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobId.cs +0 -13
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobState.cs +0 -27
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaPricing.cs +0 -33
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaProvenance.cs +0 -31
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaRequest.cs +3 -75
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaResult.cs +0 -42
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaReview.cs +0 -39
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaSeams.cs +1 -55
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaStore.cs +3 -59
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MorphMedia.cs +0 -46
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/GoogleImageProvider.cs +3 -28
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/OpenAIImageProvider.cs +7 -61
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/VeoVideoProvider.cs +2 -37
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/IUsageCostSink.cs +1 -23
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostMiddleware.cs +2 -82
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostRecord.cs +1 -68
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphOpenTelemetryExtensions.cs +1 -62
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphTelemetryOptions.cs +2 -62
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/IProviderClientFactory.cs +1 -25
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelAlias.cs +0 -124
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelRegistry.cs +1 -73
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaIngest.cs +1 -66
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaPlan.cs +0 -41
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentFactoryTests.cs +2 -112
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentSpecTests.cs +2 -43
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ConversationStoreTests.cs +5 -110
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/DeltaIngestTests.cs +7 -103
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalAssertionTests.cs +1 -42
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheMissTests.cs +1 -30
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheStampTests.cs +1 -19
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFailuresTests.cs +0 -48
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFixtureTests.cs +1 -29
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalRefreshTests.cs +2 -38
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalWorkspace.cs +1 -14
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/KitEvals.cs +4 -69
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/OfflineChatClientTests.cs +1 -10
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClient.cs +1 -57
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClientTests.cs +0 -26
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeClock.cs +1 -19
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeMediaProvider.cs +0 -32
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeProviderClientFactory.cs +0 -14
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeUsageCostSink.cs +0 -24
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/InMemoryMediaStore.cs +0 -32
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAccessibilityTests.cs +0 -37
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAliasIsNotAnAgentTests.cs +0 -24
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaDisclosureTests.cs +0 -78
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaGeneratorTests.cs +0 -53
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaIdempotencyKeyTests.cs +0 -44
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaJobStateTests.cs +0 -44
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaReviewTests.cs +0 -19
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaStoreTests.cs +0 -24
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ModelRegistryTests.cs +6 -48
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/SkeletonSmokeTests.cs +1 -13
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/TelemetryTests.cs +2 -71
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/UsageCostMiddlewareTests.cs +5 -100
- package/framework/templates/frontend/nextjs/Dockerfile.nextjs.hbs +39 -43
- package/framework/templates/frontend/nextjs/env.mjs.hbs +29 -32
- package/framework/templates/frontend/nextjs/page.tsx.hbs +21 -22
- package/framework/templates/frontend/nextjs/use-feature.ts.hbs +42 -54
- package/framework/templates/integrations/asaas-client.cs +332 -387
- package/framework/templates/integrations/asaas-webhook.cs +290 -351
- package/framework/templates/integrations/neon-auth-config.cs +9 -105
- package/framework/templates/project/validate.js +97 -120
- package/package.json +2 -2
- package/src/commands/dashboard/dashboard.js +80 -53
- package/src/commands/mcp/mcp-setup.js +37 -9
- package/src/commands/project/doctor.js +12 -6
- package/src/commands/project/init.js +6 -3
- package/src/commands/project/install-plugin-cmd.js +1 -1
- package/src/commands/project/update.js +5 -0
- package/src/commands/project/worktree.js +60 -4
- package/src/commands/state/gate-check.js +5 -0
- package/src/commands/tasks/dag.js +27 -4
- package/src/commands/validation/verify.js +22 -38
- package/src/dashboard/app/App.jsx +80 -16
- package/src/dashboard/app/components/ActivityFeed.jsx +172 -0
- package/src/dashboard/app/components/CommandPalette.jsx +155 -0
- package/src/dashboard/app/components/DetailPanel.jsx +47 -11
- package/src/dashboard/app/components/EntityLink.jsx +55 -0
- package/src/dashboard/app/components/ErrorBoundary.jsx +54 -0
- package/src/dashboard/app/components/FlowGraph.jsx +19 -17
- package/src/dashboard/app/components/KnowledgeMarkdown.jsx +53 -0
- package/src/dashboard/app/components/Markdown.jsx +47 -0
- package/src/dashboard/app/components/PhaseRail.jsx +111 -0
- package/src/dashboard/app/components/SearchFilter.jsx +40 -22
- package/src/dashboard/app/components/Sidebar.jsx +146 -37
- package/src/dashboard/app/hooks/useMorphQuery.js +54 -0
- package/src/dashboard/app/hooks/usePref.js +38 -0
- package/src/dashboard/app/lib/format.js +69 -0
- package/src/dashboard/app/lib/phases.js +56 -0
- package/src/dashboard/app/lib/project.js +50 -0
- package/src/dashboard/app/pages/Activity.jsx +24 -0
- package/src/dashboard/app/pages/AgentMap.jsx +371 -370
- package/src/dashboard/app/pages/FeatureDetail.jsx +604 -0
- package/src/dashboard/app/pages/Features.jsx +400 -0
- package/src/dashboard/app/pages/Guide.jsx +146 -0
- package/src/dashboard/app/pages/HookInspector.jsx +357 -322
- package/src/dashboard/app/pages/PhasePipeline.jsx +277 -408
- package/src/dashboard/app/pages/Portfolio.jsx +156 -0
- package/src/dashboard/app/pages/Rubrics.jsx +101 -0
- package/src/dashboard/app/pages/RulesMap.jsx +300 -273
- package/src/dashboard/app/pages/SkillsLibrary.jsx +483 -366
- package/src/dashboard/app/pages/Standards.jsx +407 -332
- package/src/dashboard/app/styles/index.css +187 -47
- package/src/dashboard/index.html +14 -12
- package/src/dashboard/package.json +32 -24
- package/src/dashboard/scanner/activity-scanner.js +351 -0
- package/src/dashboard/scanner/agents-scanner.js +59 -1
- package/src/dashboard/scanner/docs-scanner.js +59 -0
- package/src/dashboard/scanner/feature-status.js +10 -0
- package/src/dashboard/scanner/features-scanner.js +360 -0
- package/src/dashboard/scanner/index.js +14 -7
- package/src/dashboard/scanner/phases-scanner.js +14 -15
- package/src/dashboard/scanner/portfolio.js +153 -0
- package/src/dashboard/scanner/project-api.js +195 -0
- package/src/dashboard/scanner/rules-scanner.js +42 -37
- package/src/dashboard/scanner/skills-scanner.js +105 -43
- package/src/dashboard/scanner/standards-scanner.js +2 -0
- package/src/dashboard/vite-plugin-morph-data.js +134 -17
- package/src/lib/detectors/claude-config-detector.js +2 -7
- package/src/lib/detectors/index.js +5 -1
- package/src/lib/evaluator-report.js +22 -0
- package/src/lib/generators/settings-generator.js +208 -209
- package/src/lib/installers/mcp-installer.js +724 -719
- package/src/lib/installers/memory-installer.js +1 -2
- package/src/lib/projects/registry.js +154 -0
- package/src/lib/risk-detector.js +13 -0
- package/src/lib/tasks/tasks-json-parser.js +21 -0
- package/src/lib/validators/code-budget/code-budget-validator.js +32 -4
- package/src/lib/worktree/env-allocator.js +9 -3
- package/src/scripts/install-plugin.js +1 -1
- package/src/scripts/setup-infra.js +2 -4
- package/src/utils/agents-installer.js +108 -13
- package/src/utils/banner.js +1 -1
- package/src/commands/index.js +0 -13
- package/src/commands/project/checkpoint.js +0 -209
- package/src/core/state/phase-state-machine.js +0 -197
- package/src/dashboard/app/hooks/useDetailPanel.js +0 -15
- package/src/dashboard/app/pages/LiveState.jsx +0 -206
- package/src/dashboard/scanner/squads-scanner.js +0 -13
- package/src/dashboard/scanner/state-scanner.js +0 -85
- package/src/lib/generators/index.js +0 -10
- package/src/lib/hooks/hook-executor.js +0 -167
- package/src/lib/validators/checkpoint-persistence.js +0 -39
- package/src/lib/validators/shared/emit-validator-dispatch.js +0 -14
- package/src/lib/validators/shared/index.js +0 -12
- package/src/lib/validators/shared/issue-counter.js +0 -14
- package/src/lib/validators/shared/result-formatter.js +0 -124
|
@@ -50,8 +50,7 @@ using Microsoft.Extensions.AI;
|
|
|
50
50
|
using OpenAI.Images;
|
|
51
51
|
|
|
52
52
|
#pragma warning disable MEAI001 // IImageGenerator e experimental: a superficie pode mudar entre menores
|
|
53
|
-
// AsIImageGenerator()
|
|
54
|
-
// Sem ele, escreva o adaptador (e o kit escreve: ver a nota abaixo).
|
|
53
|
+
// AsIImageGenerator() exige o pacote Microsoft.Extensions.AI.OpenAI, a mais (ver a nota abaixo).
|
|
55
54
|
IImageGenerator generator = new ImageClient("gpt-image-2", apiKey).AsIImageGenerator();
|
|
56
55
|
|
|
57
56
|
var response = await generator.GenerateAsync(
|
|
@@ -197,35 +196,33 @@ trocar a foto de referência **é** outro pedido, mesmo com o prompt idêntico.
|
|
|
197
196
|
|
|
198
197
|
## Exemplo C# completo
|
|
199
198
|
|
|
199
|
+
O adaptador é de **provider**, não um serviço público que um handler possa chamar: quem produz bytes
|
|
200
|
+
vive atrás do enfileiramento de `ai-agents/media-pipeline.md`, e no kit esse tipo é `internal` de
|
|
201
|
+
propósito. Quatro escolhas do bloco abaixo foram impostas pelo compilador:
|
|
202
|
+
|
|
203
|
+
- **Sem `using OpenAI.Images;`.** O gerador chega injetado e nenhum tipo do SDK da OpenAI aparece no
|
|
204
|
+
arquivo; deixar o `using` torna `ImageGenerationOptions` ambíguo (CS0104) entre
|
|
205
|
+
`Microsoft.Extensions.AI` e `OpenAI.Images`. Onde os dois convivem de verdade, como no adaptador do
|
|
206
|
+
kit, o tipo vai qualificado.
|
|
207
|
+
- **O pragma abre acima da classe**, não dentro do método: o parâmetro do construtor primário também
|
|
208
|
+
nomeia `IImageGenerator`, e `MEAI001` é erro por padrão, não aviso. Pragma dentro do corpo deixa a
|
|
209
|
+
assinatura de fora e o arquivo não compila.
|
|
210
|
+
- **O `IImageGenerator` chega injetado**, não construído aqui, e é isso que faz o exemplo compilar com
|
|
211
|
+
ou sem `Microsoft.Extensions.AI.OpenAI` no pin (ver a ressalva de §A API): com o pacote,
|
|
212
|
+
`imageClient.AsIImageGenerator()`; sem ele, um `IImageGenerator` escrito à mão sobre o `ImageClient`,
|
|
213
|
+
como em `templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/OpenAIImageProvider.cs`.
|
|
214
|
+
- **`ReadOnlyMemory<byte>` dos dois lados**, porque é o tipo real de `DataContent.Data`; declarar
|
|
215
|
+
`BinaryData` não converte (CS0029).
|
|
216
|
+
|
|
217
|
+
`OriginalImages` é o gancho de edição: com referências, o produto real é preservado em vez de recriado.
|
|
218
|
+
|
|
200
219
|
```csharp
|
|
201
|
-
// SEM `using OpenAI.Images;`. Ele nao e necessario aqui — o gerador chega injetado e nenhum
|
|
202
|
-
// tipo do SDK da OpenAI aparece neste arquivo — e deixa-lo torna `ImageGenerationOptions`
|
|
203
|
-
// AMBIGUO (CS0104) entre Microsoft.Extensions.AI e OpenAI.Images. Onde os dois convivem de
|
|
204
|
-
// verdade, como no adaptador do kit, o tipo vai QUALIFICADO.
|
|
205
220
|
using Microsoft.Extensions.AI;
|
|
206
221
|
|
|
207
|
-
// O pragma abre ACIMA da classe, e nao dentro do metodo: o parametro do construtor primario
|
|
208
|
-
// tambem nomeia IImageGenerator, e MEAI001 e ERRO por padrao — nao aviso. Pragma dentro do
|
|
209
|
-
// corpo deixa a assinatura de fora e o arquivo nao compila.
|
|
210
222
|
#pragma warning disable MEAI001 // IImageGenerator/ImageGenerationRequest sao experimentais: a superficie pode mudar entre menores.
|
|
211
223
|
|
|
212
|
-
/// <summary>
|
|
213
|
-
/// Adaptador de PROVIDER. Note o que ele NAO e: um servico publico que um handler
|
|
214
|
-
/// possa chamar. Quem produz bytes vive atras do enfileiramento descrito em
|
|
215
|
-
/// ai-agents/media-pipeline.md, e no kit esse tipo e `internal` de proposito.
|
|
216
|
-
/// </summary>
|
|
217
|
-
/// <remarks>
|
|
218
|
-
/// O IImageGenerator chega INJETADO, e nao construido aqui. As duas formas de obte-lo
|
|
219
|
-
/// sao legitimas e a escolha e do seu pin (ver a ressalva da secao "A API"):
|
|
220
|
-
/// - com Microsoft.Extensions.AI.OpenAI no pin: imageClient.AsIImageGenerator()
|
|
221
|
-
/// - sem ele: um IImageGenerator escrito a mao sobre o ImageClient, como em
|
|
222
|
-
/// templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/OpenAIImageProvider.cs
|
|
223
|
-
/// Injetar em vez de construir e o que faz ESTE exemplo compilar nos dois casos.
|
|
224
|
-
/// </remarks>
|
|
225
224
|
internal sealed class OpenAiImageAdapter(IImageGenerator generator)
|
|
226
225
|
{
|
|
227
|
-
// ReadOnlyMemory<byte> dos dois lados porque e o tipo REAL de DataContent.Data.
|
|
228
|
-
// Declarar BinaryData aqui nao converte (CS0029) — e o compilador que decide isso.
|
|
229
226
|
public async Task<ReadOnlyMemory<byte>> ProduceAsync(
|
|
230
227
|
string prompt,
|
|
231
228
|
IReadOnlyList<ReadOnlyMemory<byte>> references,
|
|
@@ -234,7 +231,6 @@ internal sealed class OpenAiImageAdapter(IImageGenerator generator)
|
|
|
234
231
|
var request = new ImageGenerationRequest { Prompt = prompt };
|
|
235
232
|
if (references.Count > 0)
|
|
236
233
|
{
|
|
237
|
-
// OriginalImages e o gancho de EDICAO: preserva o produto real em vez de recria-lo.
|
|
238
234
|
request.OriginalImages = [.. references.Select(r => new DataContent(r, "image/png"))];
|
|
239
235
|
}
|
|
240
236
|
|
|
@@ -49,6 +49,8 @@ O projeto injeta `AudioClient` via DI. Para transcrição com timestamps (ex: si
|
|
|
49
49
|
|
|
50
50
|
## Exemplo C# completo
|
|
51
51
|
|
|
52
|
+
Três entradas: arquivo, bytes (ex: upload do usuário) — as duas devolvem texto simples — e a verbose, com timestamps por palavra e segmento, útil para legendas ou busca por trecho.
|
|
53
|
+
|
|
52
54
|
```csharp
|
|
53
55
|
using OpenAI.Audio;
|
|
54
56
|
using Microsoft.Extensions.Configuration;
|
|
@@ -64,19 +66,12 @@ public sealed class TranscriptionService
|
|
|
64
66
|
_client = new AudioClient(model, apiKey);
|
|
65
67
|
}
|
|
66
68
|
|
|
67
|
-
/// <summary>
|
|
68
|
-
/// Transcreve áudio a partir de um arquivo e retorna texto simples.
|
|
69
|
-
/// O transcript retornado pode ser passado diretamente como input para um AIAgent downstream.
|
|
70
|
-
/// </summary>
|
|
71
69
|
public async Task<string> TranscribeAsync(string audioFilePath, CancellationToken ct = default)
|
|
72
70
|
{
|
|
73
71
|
AudioTranscription transcription = await _client.TranscribeAudioAsync(audioFilePath, cancellationToken: ct);
|
|
74
72
|
return transcription.Text;
|
|
75
73
|
}
|
|
76
74
|
|
|
77
|
-
/// <summary>
|
|
78
|
-
/// Transcreve áudio a partir de bytes (ex: upload do usuário) e retorna texto simples.
|
|
79
|
-
/// </summary>
|
|
80
75
|
public async Task<string> TranscribeBytesAsync(
|
|
81
76
|
Stream audioStream,
|
|
82
77
|
string fileName,
|
|
@@ -93,9 +88,6 @@ public sealed class TranscriptionService
|
|
|
93
88
|
return transcription.Text;
|
|
94
89
|
}
|
|
95
90
|
|
|
96
|
-
/// <summary>
|
|
97
|
-
/// Transcreve com timestamps por palavra e segmento (útil para legendas ou busca por trecho).
|
|
98
|
-
/// </summary>
|
|
99
91
|
public async Task<AudioTranscription> TranscribeVerboseAsync(string audioFilePath, CancellationToken ct = default)
|
|
100
92
|
{
|
|
101
93
|
var options = new AudioTranscriptionOptions
|
|
@@ -109,21 +101,18 @@ public sealed class TranscriptionService
|
|
|
109
101
|
}
|
|
110
102
|
```
|
|
111
103
|
|
|
112
|
-
**Alimentando um agente downstream com o transcript
|
|
104
|
+
**Alimentando um agente downstream com o transcript** — ele é texto puro, então basta passá-lo como input para qualquer `AIAgent`; aqui, um Status Agent que extrai as ações da reunião:
|
|
113
105
|
|
|
114
106
|
```csharp
|
|
115
|
-
// O transcript é texto puro — basta passar como input para qualquer AIAgent
|
|
116
107
|
string transcript = await _transcription.TranscribeAsync("meeting-2024-05-19.mp3");
|
|
117
108
|
|
|
118
|
-
// Ex: Status Agent que extrai ações da reunião
|
|
119
109
|
AgentResponse<MeetingActions> actions = await _statusAgent.RunAsync<MeetingActions>(
|
|
120
110
|
$"Extraia ações, decisões e responsáveis desta transcrição:\n\n{transcript}");
|
|
121
111
|
```
|
|
122
112
|
|
|
123
|
-
**Registro DI
|
|
113
|
+
**Registro DI** (`Program.cs`):
|
|
124
114
|
|
|
125
115
|
```csharp
|
|
126
|
-
// Program.cs
|
|
127
116
|
builder.Services.AddSingleton<TranscriptionService>();
|
|
128
117
|
```
|
|
129
118
|
|
|
@@ -58,17 +58,11 @@ public sealed class TextToSpeechService
|
|
|
58
58
|
|
|
59
59
|
public TextToSpeechService(IConfiguration config)
|
|
60
60
|
{
|
|
61
|
-
// tts-1: mais rápido, menor qualidade. tts-1-hd: mais lento, maior qualidade.
|
|
62
61
|
var model = config["TTS:Model"] ?? "tts-1";
|
|
63
62
|
var apiKey = config["OpenAI:ApiKey"]!;
|
|
64
63
|
_client = new AudioClient(model, apiKey);
|
|
65
64
|
}
|
|
66
65
|
|
|
67
|
-
/// <summary>
|
|
68
|
-
/// Converte texto em áudio e retorna bytes MP3.
|
|
69
|
-
/// Cachear o resultado é altamente recomendado para textos repetitivos — gerar o mesmo texto
|
|
70
|
-
/// duas vezes é desperdício de custo e latência.
|
|
71
|
-
/// </summary>
|
|
72
66
|
public async Task<BinaryData> SynthesizeAsync(
|
|
73
67
|
string text,
|
|
74
68
|
GeneratedSpeechVoice? voice = null,
|
|
@@ -91,14 +85,13 @@ public sealed class TextToSpeechService
|
|
|
91
85
|
}
|
|
92
86
|
```
|
|
93
87
|
|
|
94
|
-
**Registro DI
|
|
88
|
+
**Registro DI** (`Program.cs`):
|
|
95
89
|
|
|
96
90
|
```csharp
|
|
97
|
-
// Program.cs
|
|
98
91
|
builder.Services.AddSingleton<TextToSpeechService>();
|
|
99
92
|
```
|
|
100
93
|
|
|
101
|
-
**Servindo como download ou stream
|
|
94
|
+
**Servindo como download ou stream** — verifique o cache antes de gerar (ver Anti-patterns):
|
|
102
95
|
|
|
103
96
|
```csharp
|
|
104
97
|
public class AudioController(TextToSpeechService tts) : ControllerBase
|
|
@@ -106,14 +99,12 @@ public class AudioController(TextToSpeechService tts) : ControllerBase
|
|
|
106
99
|
[HttpPost("synthesize")]
|
|
107
100
|
public async Task<IActionResult> Synthesize([FromBody] SynthesizeRequest req)
|
|
108
101
|
{
|
|
109
|
-
// Verificar cache antes de gerar (ver Anti-patterns)
|
|
110
102
|
BinaryData audio = await tts.SynthesizeAsync(
|
|
111
103
|
text: req.Text,
|
|
112
104
|
voice: GeneratedSpeechVoice.Nova,
|
|
113
105
|
format: GeneratedSpeechFormat.Mp3,
|
|
114
106
|
speedRatio: 1.1f);
|
|
115
107
|
|
|
116
|
-
// the storage layer may assign the final name; use a timestamp-based name here
|
|
117
108
|
var fileName = $"tts-{DateTime.UtcNow:yyyyMMddHHmmss}.mp3";
|
|
118
109
|
return File(audio.ToStream(), "audio/mpeg", fileName);
|
|
119
110
|
}
|
|
@@ -31,7 +31,6 @@ Vision em MAF usa `ChatMessage` multimodal — a mesma abstração `IChatClient`
|
|
|
31
31
|
```csharp
|
|
32
32
|
using Microsoft.Extensions.AI;
|
|
33
33
|
|
|
34
|
-
// Constrói um ChatMessage com texto + imagem
|
|
35
34
|
ChatMessage message = new(ChatRole.User, [
|
|
36
35
|
new TextContent("O que aparece nesta imagem?"),
|
|
37
36
|
new UriContent(new Uri("https://example.com/foto.jpg"), "image/jpeg")
|
|
@@ -59,7 +58,7 @@ O `IChatClient` (e portanto `AIAgent`) aceita `IEnumerable<ChatMessage>` — voc
|
|
|
59
58
|
|
|
60
59
|
## Exemplo C# completo
|
|
61
60
|
|
|
62
|
-
Agente de visão que recebe URL de imagem e retorna descrição. Para extração de dados tipados de um documento, combine com `RunAsync<T>` (
|
|
61
|
+
Agente de visão que recebe URL de imagem e retorna descrição. Três entradas: URL pública, arquivo local via `DataContent.LoadFromAsync` (o padrão canônico MAF, preferido sempre que houver o caminho do arquivo) e bytes já em memória (ex: upload de usuário já lido). Para extração de dados tipados de um documento, combine com `RunAsync<T>` em vez de `RunAsync` — `AgentResponse<InvoiceData> r = await _agent.RunAsync<InvoiceData>(message);` e `return r.Result;` —, com o padrão completo em `ai-agents-structured-output`.
|
|
63
62
|
|
|
64
63
|
```csharp
|
|
65
64
|
using Microsoft.Agents.AI;
|
|
@@ -84,7 +83,6 @@ public sealed class VisionAgent
|
|
|
84
83
|
});
|
|
85
84
|
}
|
|
86
85
|
|
|
87
|
-
/// <summary>Analisa uma imagem por URL pública e retorna descrição em texto.</summary>
|
|
88
86
|
public async Task<string> AnalyzeImageUrlAsync(Uri imageUri, string question, CancellationToken ct = default)
|
|
89
87
|
{
|
|
90
88
|
ChatMessage message = new(ChatRole.User, [
|
|
@@ -96,14 +94,6 @@ public sealed class VisionAgent
|
|
|
96
94
|
return response.Text;
|
|
97
95
|
}
|
|
98
96
|
|
|
99
|
-
/// <summary>
|
|
100
|
-
/// Analisa imagem a partir de um arquivo local ou caminho de upload.
|
|
101
|
-
/// Usa DataContent.LoadFromAsync — padrão canônico MAF para carregar imagem de arquivo.
|
|
102
|
-
/// Para extrair dados tipados, use RunAsync<T> em vez de RunAsync:
|
|
103
|
-
/// AgentResponse<InvoiceData> r = await _agent.RunAsync<InvoiceData>(message);
|
|
104
|
-
/// return r.Result;
|
|
105
|
-
/// Veja ai-agents-structured-output para o padrão completo.
|
|
106
|
-
/// </summary>
|
|
107
97
|
public async Task<string> AnalyzeImageFileAsync(string filePath, string question, CancellationToken ct = default)
|
|
108
98
|
{
|
|
109
99
|
DataContent imageContent = await DataContent.LoadFromAsync(filePath);
|
|
@@ -116,10 +106,6 @@ public sealed class VisionAgent
|
|
|
116
106
|
return response.Text;
|
|
117
107
|
}
|
|
118
108
|
|
|
119
|
-
/// <summary>
|
|
120
|
-
/// Analisa imagem a partir de bytes já em memória (ex: upload de usuário já lido).
|
|
121
|
-
/// Use AnalyzeImageFileAsync quando tiver o caminho do arquivo — é o padrão preferido.
|
|
122
|
-
/// </summary>
|
|
123
109
|
public async Task<string> AnalyzeImageBytesAsync(BinaryData imageData, string mediaType, string question, CancellationToken ct = default)
|
|
124
110
|
{
|
|
125
111
|
ChatMessage message = new(ChatRole.User, [
|
|
@@ -138,17 +124,14 @@ public sealed class VisionAgent
|
|
|
138
124
|
```csharp
|
|
139
125
|
var agent = new VisionAgent(modelRegistry);
|
|
140
126
|
|
|
141
|
-
// Por URL pública
|
|
142
127
|
string desc = await agent.AnalyzeImageUrlAsync(
|
|
143
128
|
new Uri("https://cdn.example.com/invoice-2024.jpg"),
|
|
144
129
|
"Extraia os campos: número da nota, valor total e data de emissão.");
|
|
145
130
|
|
|
146
|
-
// Por arquivo local — padrão canônico com DataContent.LoadFromAsync
|
|
147
131
|
string desc2 = await agent.AnalyzeImageFileAsync(
|
|
148
132
|
"nota-fiscal.png",
|
|
149
133
|
"Liste todos os itens e valores desta nota fiscal.");
|
|
150
134
|
|
|
151
|
-
// Por bytes em memória (ex: upload já lido de IFormFile)
|
|
152
135
|
BinaryData data = await BinaryData.FromStreamAsync(formFile.OpenReadStream());
|
|
153
136
|
string desc3 = await agent.AnalyzeImageBytesAsync(data, "image/png",
|
|
154
137
|
"Descreva o conteúdo da imagem enviada.");
|
|
@@ -113,10 +113,6 @@ Interface contract:
|
|
|
113
113
|
```csharp
|
|
114
114
|
public interface IPromptRepository
|
|
115
115
|
{
|
|
116
|
-
/// <summary>
|
|
117
|
-
/// Returns the content of the currently active prompt for the given agent key.
|
|
118
|
-
/// Throws <see cref="InvalidOperationException"/> if no active prompt exists.
|
|
119
|
-
/// </summary>
|
|
120
116
|
Task<string> GetActivePromptAsync(string agentKey, CancellationToken ct = default);
|
|
121
117
|
}
|
|
122
118
|
```
|
|
@@ -9,8 +9,24 @@
|
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
**O padrão é nenhum comentário.** O código se explica pelos nomes, pelos tipos e pelos testes. Um
|
|
13
|
+
comentário é relido a cada leitura do arquivo, por quem mantém e pelo agente que abre o arquivo na
|
|
14
|
+
próxima task, e ninguém o atualiza quando o código muda. Ele só entra quando diz algo que o código
|
|
15
|
+
**não consegue** dizer, e isso é raro: a maioria dos arquivos não tem nenhum.
|
|
16
|
+
|
|
17
|
+
## Antes de escrever um comentário
|
|
18
|
+
|
|
19
|
+
Tente, nesta ordem, e pare no primeiro que resolve:
|
|
20
|
+
|
|
21
|
+
| Em vez de comentar... | Faça |
|
|
22
|
+
|---|---|
|
|
23
|
+
| o que uma variável ou função é | renomeie: `retryDelayMs`, `isEligibleForRefund()` |
|
|
24
|
+
| o que um trecho faz | extraia uma função com esse nome |
|
|
25
|
+
| um número ou string mágica | extraia uma constante nomeada |
|
|
26
|
+
| um estado que não pode acontecer | expresse no tipo (enum, união, record, `required`) |
|
|
27
|
+
| o que o código garante | escreva um teste que falha quando deixar de valer |
|
|
28
|
+
|
|
29
|
+
Só sobrando algo que nenhum desses expressa, o comentário entra.
|
|
14
30
|
|
|
15
31
|
## O que merece comentário
|
|
16
32
|
|
|
@@ -21,15 +37,22 @@ Só o **porquê que o código não mostra**:
|
|
|
21
37
|
| Invariante que o tipo não expressa | "a chave é a identidade e o hash é a versão" |
|
|
22
38
|
| Ordem que importa | "invalidar o cache ANTES de publicar: o consumidor relê na hora" |
|
|
23
39
|
| Peculiaridade externa | API de terceiro que devolve 200 com erro no corpo; bug de biblioteca, com o link |
|
|
24
|
-
| Contrato de
|
|
40
|
+
| Contrato de pacote publicado | `///` ou JSDoc só em API de pacote consumido **fora** deste repositório (NuGet, npm) |
|
|
25
41
|
|
|
26
42
|
## O que não merece
|
|
27
43
|
|
|
28
44
|
- **História de processo.** Task, UC, ADR, FR, Gate, avaliador, `spec.md`, mockup, "confirmado ao
|
|
29
45
|
implementar a T11", "o Gate 3 pediu". Isso vai na mensagem de commit e no `decisions.md`, onde tem
|
|
30
46
|
data e autor e não envelhece dentro do arquivo.
|
|
31
|
-
- **Repetição do código.** `// incrementa o contador` sobre `count
|
|
32
|
-
|
|
47
|
+
- **Repetição do código.** `// incrementa o contador` sobre `count++`, `// valida o request` sobre
|
|
48
|
+
`validator.Validate(request)`. Nome bom substitui comentário.
|
|
49
|
+
- **Doc-comment que repete a assinatura.** `/// <summary>Obtém o pedido.</summary>` sobre
|
|
50
|
+
`GetOrder()`, JSDoc com `@param id o id`. Membro de aplicação (handler, endpoint, componente,
|
|
51
|
+
serviço interno) não leva `///` nem JSDoc, mesmo sendo `public`.
|
|
52
|
+
- **Estrutura e decoração.** Cabeçalho de arquivo, banner de seção (`// ---- Handlers ----`,
|
|
53
|
+
`#region`), comentário que só rotula o bloco seguinte.
|
|
54
|
+
- **Código comentado e TODO solto.** O git guarda o código antigo; pendência vira issue, e o
|
|
55
|
+
comentário, se ficar, cita o número dela.
|
|
33
56
|
- **Estado do mundo fora do arquivo.** "Public Sans é a fonte da interface" passa a mentir na
|
|
34
57
|
primeira troca de fonte, e nada avisa.
|
|
35
58
|
- **Afirmação sobre comportamento.** "nunca retorna vazio", "ordenado por sobrenome": isso é
|
|
@@ -37,27 +60,40 @@ Só o **porquê que o código não mostra**:
|
|
|
37
60
|
|
|
38
61
|
## Tamanho
|
|
39
62
|
|
|
40
|
-
|
|
41
|
-
terceiro com a reprodução). Bloco longo narrando decisões é `decisions.md` com
|
|
63
|
+
Uma linha é o normal; teto de ~3. Acima disso o comentário justifica o volume (algoritmo não
|
|
64
|
+
óbvio, armadilha de terceiro com a reprodução). Bloco longo narrando decisões é `decisions.md` com
|
|
65
|
+
outro nome. Em volume, código de aplicação fica perto de zero: mais de uma linha de comentário a
|
|
66
|
+
cada dez de código é sinal de que os nomes não estão fazendo o trabalho.
|
|
42
67
|
|
|
43
68
|
## Exemplo
|
|
44
69
|
|
|
45
70
|
```ts
|
|
46
|
-
// Ruim: conta a task, repete o código e afirma comportamento que nenhum teste cobra.
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
//
|
|
71
|
+
// Ruim: rotula, conta a task, repete o código e afirma comportamento que nenhum teste cobra.
|
|
72
|
+
/**
|
|
73
|
+
* Salva o documento.
|
|
74
|
+
* @param doc o documento
|
|
75
|
+
*/
|
|
76
|
+
// T8 + T11: mutação obrigatória do relatório, item c. Faz upsert e nunca duplica.
|
|
77
|
+
async function save(doc: Doc) {
|
|
78
|
+
await upsert({ key: doc.sourceId, hash: sha256(doc.body) });
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Bom: sem comentário nenhum, porque o nome e a chamada já dizem tudo...
|
|
82
|
+
async function save(doc: Doc) {
|
|
83
|
+
await upsert({ key: doc.sourceId, hash: sha256(doc.body) });
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// ...e um comentário só quando há um porquê invisível.
|
|
52
87
|
// A chave é a identidade e o hash é a versão: hash novo na mesma chave substitui.
|
|
53
88
|
await upsert({ key: doc.sourceId, hash: sha256(doc.body) });
|
|
54
89
|
```
|
|
55
90
|
|
|
56
91
|
## Na revisão
|
|
57
92
|
|
|
58
|
-
Comentário
|
|
59
|
-
`morph-spec verify` mede, nas linhas que a feature adicionou, quantas
|
|
60
|
-
|
|
93
|
+
Comentário que o código dispensava é achado da rubrica `code-quality` no Gate 3. O nó `validators`
|
|
94
|
+
do `morph-spec verify` mede, nas linhas que a feature adicionou (validador `code-budget`), quantas
|
|
95
|
+
linhas de comentário citam processo e a densidade de comentário sobre o código adicionado, e avisa
|
|
96
|
+
com os números e os arquivos mais comentados.
|
|
61
97
|
|
|
62
98
|
---
|
|
63
99
|
|
|
@@ -128,8 +128,10 @@ regression test per bug fixed**. Going above it takes a one-line justification i
|
|
|
128
128
|
- **Shared fixture by default.** Reuse the project's `WebApplicationFactory` fixture, fakes and
|
|
129
129
|
builders. Before writing a new helper, look for the existing one (`morph-spec graph explain
|
|
130
130
|
<Name>`; fallback without a graph: `grep -rn`).
|
|
131
|
-
- **
|
|
132
|
-
|
|
131
|
+
- **Order is free, proof is not the implementer's:** a bug gets a regression test, and pure logic
|
|
132
|
+
gets its tests, in whatever order the code comes out. The implementer does not run red→green
|
|
133
|
+
cycles nor mutate production code to watch a test fail; it runs its own filter once before the
|
|
134
|
+
report, and the Gate 3 mutation proof is what shows the test bites.
|
|
133
135
|
- **A surviving mutant** of the Gate 3 mutation proof is killed by tightening an existing assertion
|
|
134
136
|
or adding a table row, not by one new test per mutant.
|
|
135
137
|
|