@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
|
@@ -1,24 +1,5 @@
|
|
|
1
1
|
namespace Morph.AiKit.Media;
|
|
2
2
|
|
|
3
|
-
/// <summary>
|
|
4
|
-
/// O que um provider produz: bytes, mais o que so o provider sabe sobre a geracao.
|
|
5
|
-
/// </summary>
|
|
6
|
-
/// <remarks>
|
|
7
|
-
/// <b>Este tipo nunca sai pela superficie publica do subsistema.</b> Ele existe entre o
|
|
8
|
-
/// <c>IMediaProvider</c> (internal) e o worker (internal); o que chega ao projeto e um
|
|
9
|
-
/// <see cref="MediaJob"/> com caminho e hash. Se um handler consegue segurar um
|
|
10
|
-
/// <see cref="MediaArtifact"/>, a regra dura de ai-agents/media-pipeline.md foi furada.
|
|
11
|
-
/// </remarks>
|
|
12
|
-
/// <param name="Kind">Imagem ou video.</param>
|
|
13
|
-
/// <param name="MediaType">MIME dos bytes devolvidos pelo provider.</param>
|
|
14
|
-
/// <param name="Bytes">O conteudo. Baixado JA, nunca uma URL guardada para depois.</param>
|
|
15
|
-
/// <param name="RevisedPrompt">O prompt que o provider reescreveu, quando ele devolve um.</param>
|
|
16
|
-
/// <param name="ResponseId">O id do lado do provider, para abrir chamado.</param>
|
|
17
|
-
/// <param name="C2paPreserved">
|
|
18
|
-
/// Se os metadados de proveniencia do provider sobreviveram ao caminho ate aqui. Falso
|
|
19
|
-
/// quando o adaptador re-encodou os bytes — e um re-encode destrutivo apaga a credencial
|
|
20
|
-
/// C2PA, que e a unica prova tecnica de origem que a imagem carregava.
|
|
21
|
-
/// </param>
|
|
22
3
|
internal sealed record MediaArtifact(
|
|
23
4
|
MediaKind Kind,
|
|
24
5
|
string MediaType,
|
|
@@ -1,44 +1,8 @@
|
|
|
1
1
|
namespace Morph.AiKit.Media;
|
|
2
2
|
|
|
3
|
-
/// <summary>
|
|
4
|
-
/// A marcação "gerado/editado por IA" que acompanha a publicação.
|
|
5
|
-
/// </summary>
|
|
6
|
-
/// <remarks>
|
|
7
|
-
/// <para>
|
|
8
|
-
/// <b>Este tipo existe porque a afirmação estava só na prosa.</b> Antes dele,
|
|
9
|
-
/// <see cref="MediaJobState.Published"/> dizia "publicado, com a marcacao aplicada" e
|
|
10
|
-
/// <see cref="MediaPurpose.Marketing"/> dizia "marcacao visivel obrigatoria" — e um
|
|
11
|
-
/// <c>grep</c> por label, watermark ou disclosure no kit inteiro voltava <b>vazio</b>. Três
|
|
12
|
-
/// das quatro afirmações que a rubrica <c>media-claims</c> cobra de terceiros estavam presas
|
|
13
|
-
/// por tipo ou por estado; a quarta, que é a obrigação legal, estava presa por nada.
|
|
14
|
-
/// </para>
|
|
15
|
-
/// <para>
|
|
16
|
-
/// <b>O construtor é PRIVADO, e isso não é estilo.</b> A primeira versão deste tipo era um
|
|
17
|
-
/// record POSICIONAL — e um record posicional gera um construtor <b>público</b>. A validação
|
|
18
|
-
/// morava só em <see cref="Create"/>, então <c>new MediaDisclosure("", "", at)</c> compilava,
|
|
19
|
-
/// era aceito, e publicava com rótulo vazio. Medido de um consumidor externo:
|
|
20
|
-
/// <c>IsSuccess=True State=Published</c>, <c>Text='' Surface=''</c>. A prosa dizia "recusado na
|
|
21
|
-
/// construção" em quatro superfícies; a construção não recusava nada. Uma marcação vazia é
|
|
22
|
-
/// exatamente o campo preenchido "para passar" que este tipo existe para impedir, e agora é o
|
|
23
|
-
/// <b>compilador</b> que fecha a porta — não a documentação. Mesma forma de
|
|
24
|
-
/// <see cref="MediaReview"/>, três arquivos ao lado.
|
|
25
|
-
/// </para>
|
|
26
|
-
/// <para>
|
|
27
|
-
/// <b>O que este tipo prova, exatamente:</b> que um job <b>não alcança</b>
|
|
28
|
-
/// <see cref="MediaJobState.Published"/> sem uma marcação registrada e não-vazia, quando a
|
|
29
|
-
/// finalidade a exige. É o mesmo mecanismo de <see cref="MediaJob.Approve"/> — uma transição
|
|
30
|
-
/// que o código não tem, em vez de uma frase que alguém tem de lembrar.
|
|
31
|
-
/// </para>
|
|
32
|
-
/// <para>
|
|
33
|
-
/// <b>O que ele NÃO prova, e é honesto dizer:</b> que o rótulo chegou aos olhos de alguém. A
|
|
34
|
-
/// renderização é do aplicativo consumidor, e nenhum tipo em C# alcança um pixel. O que o kit
|
|
35
|
-
/// garante é que a decisão foi tomada, com texto e superfície nomeados, e ficou gravada com o
|
|
36
|
-
/// artefato — de modo que "esquecemos de marcar" deixa de ser um caminho silencioso e passa a
|
|
37
|
-
/// ser uma recusa na publicação.
|
|
38
|
-
/// </para>
|
|
39
|
-
/// </remarks>
|
|
40
3
|
public sealed record MediaDisclosure
|
|
41
4
|
{
|
|
5
|
+
// Privado de proposito: um record posicional geraria construtor publico que pula a validacao de Create.
|
|
42
6
|
private MediaDisclosure(string text, string surface, DateTimeOffset appliedAt)
|
|
43
7
|
{
|
|
44
8
|
Text = text;
|
|
@@ -46,25 +10,12 @@ public sealed record MediaDisclosure
|
|
|
46
10
|
AppliedAt = appliedAt;
|
|
47
11
|
}
|
|
48
12
|
|
|
49
|
-
/// <summary>
|
|
50
|
-
/// O texto visível, por extenso. Em branco é recusado — e a recusa é presa pelo construtor
|
|
51
|
-
/// privado, não por convenção de chamada.
|
|
52
|
-
/// </summary>
|
|
53
13
|
public string Text { get; }
|
|
54
14
|
|
|
55
|
-
/// <summary>
|
|
56
|
-
/// ONDE o rótulo aparece — legenda, sobreposição, texto alternativo, ficha do produto. Sem
|
|
57
|
-
/// isto, "marcado" não diz se alguém consegue ver.
|
|
58
|
-
/// </summary>
|
|
59
15
|
public string Surface { get; }
|
|
60
16
|
|
|
61
|
-
/// <summary>Quando a marcação foi decidida.</summary>
|
|
62
17
|
public DateTimeOffset AppliedAt { get; }
|
|
63
18
|
|
|
64
|
-
/// <summary>
|
|
65
|
-
/// A ÚNICA forma de obter uma marcação. Recusa texto ou superfície em branco.
|
|
66
|
-
/// </summary>
|
|
67
|
-
/// <exception cref="ArgumentException">Texto ou superfície vazios.</exception>
|
|
68
19
|
public static MediaDisclosure Create(string text, string surface, DateTimeOffset appliedAt)
|
|
69
20
|
{
|
|
70
21
|
ArgumentException.ThrowIfNullOrWhiteSpace(text);
|
|
@@ -74,34 +25,11 @@ public sealed record MediaDisclosure
|
|
|
74
25
|
}
|
|
75
26
|
}
|
|
76
27
|
|
|
77
|
-
/// <summary>Política de marcação por finalidade. Escrita uma vez, aqui.</summary>
|
|
78
28
|
public static class MediaPurposeExtensions
|
|
79
29
|
{
|
|
80
|
-
|
|
81
|
-
/// Se a finalidade exige marcação visível para publicar.
|
|
82
|
-
/// </summary>
|
|
83
|
-
/// <remarks>
|
|
84
|
-
/// <para>
|
|
85
|
-
/// <b>Catálogo e marketing exigem; rascunho não publica.</b> A regra do art. 50 do AI Act
|
|
86
|
-
/// mira conteúdo que <i>possa parecer autêntico</i> sobre produto, pessoa ou lugar — e é
|
|
87
|
-
/// exatamente o que catálogo e peça de campanha são. A leitura conservadora é do standard
|
|
88
|
-
/// <c>ai-agents/media-pipeline.md</c>, seção Marcação, com a ressalva de fonte que ele
|
|
89
|
-
/// carrega: este predicado é uma decisão de desenho, não parecer jurídico.
|
|
90
|
-
/// </para>
|
|
91
|
-
/// <para>
|
|
92
|
-
/// Não confunda com <see cref="IsPublishable"/>: um rascunho não chega a publicar, então
|
|
93
|
-
/// a pergunta "precisa de marcação" nem se coloca para ele.
|
|
94
|
-
/// </para>
|
|
95
|
-
/// </remarks>
|
|
30
|
+
// AI Act art. 50, na leitura conservadora de ai-agents/media-pipeline.md (decisao de desenho, nao parecer juridico).
|
|
96
31
|
public static bool RequiresDisclosure(this MediaPurpose purpose)
|
|
97
32
|
=> purpose is MediaPurpose.CatalogPhoto or MediaPurpose.Marketing;
|
|
98
33
|
|
|
99
|
-
/// <summary>
|
|
100
|
-
/// Se a finalidade pode chegar a <see cref="MediaJobState.Published"/>.
|
|
101
|
-
/// </summary>
|
|
102
|
-
/// <remarks>
|
|
103
|
-
/// <see cref="MediaPurpose.Draft"/> se declara "nunca publicável" desde o primeiro dia — e
|
|
104
|
-
/// até este tipo existir, isso era só um comentário XML. Agora é uma recusa.
|
|
105
|
-
/// </remarks>
|
|
106
34
|
public static bool IsPublishable(this MediaPurpose purpose) => purpose != MediaPurpose.Draft;
|
|
107
35
|
}
|
|
@@ -2,17 +2,6 @@ using Morph.AiKit.Media.Providers;
|
|
|
2
2
|
|
|
3
3
|
namespace Morph.AiKit.Media;
|
|
4
4
|
|
|
5
|
-
/// <summary>
|
|
6
|
-
/// A implementacao de <see cref="IMediaGenerator"/>. <b><c>internal</c></b>: o projeto
|
|
7
|
-
/// enxerga a interface, nunca o tipo.
|
|
8
|
-
/// </summary>
|
|
9
|
-
/// <remarks>
|
|
10
|
-
/// A ORDEM do <see cref="EnqueueAsync"/> e o contrato, e ela e economia, nao estilo:
|
|
11
|
-
/// validar, resolver alias, derivar a chave de PEDIDO, <b>consultar a chave antes de
|
|
12
|
-
/// qualquer coisa paga</b>, so entao estimar custo e reservar teto, e por ultimo gravar o
|
|
13
|
-
/// job. Um retry que chega aqui reencontra o job e nao paga de novo; um estouro de teto
|
|
14
|
-
/// recusa antes de o dinheiro sair.
|
|
15
|
-
/// </remarks>
|
|
16
5
|
internal sealed class MediaGenerator : IMediaGenerator
|
|
17
6
|
{
|
|
18
7
|
private readonly IMediaAliasResolver _aliases;
|
|
@@ -36,7 +25,6 @@ internal sealed class MediaGenerator : IMediaGenerator
|
|
|
36
25
|
_clock = clock;
|
|
37
26
|
}
|
|
38
27
|
|
|
39
|
-
/// <inheritdoc />
|
|
40
28
|
public async Task<MediaResult<MediaJobId>> EnqueueAsync(
|
|
41
29
|
MediaRequest request,
|
|
42
30
|
CancellationToken cancellationToken = default)
|
|
@@ -64,8 +52,7 @@ internal sealed class MediaGenerator : IMediaGenerator
|
|
|
64
52
|
|
|
65
53
|
var key = MediaIdempotencyKey.From(request, binding.Value.Provider, binding.Value.Model);
|
|
66
54
|
|
|
67
|
-
// ANTES de qualquer coisa paga
|
|
68
|
-
// existente; so um job em Failed vale outra tentativa.
|
|
55
|
+
// ANTES de qualquer coisa paga; so um job em Failed vale outra tentativa.
|
|
69
56
|
var existing = await _jobs.FindByIdempotencyKeyAsync(key, cancellationToken).ConfigureAwait(false);
|
|
70
57
|
if (existing is not null && existing.State != MediaJobState.Failed)
|
|
71
58
|
{
|
|
@@ -99,7 +86,6 @@ internal sealed class MediaGenerator : IMediaGenerator
|
|
|
99
86
|
return MediaResult<MediaJobId>.Ok(job.Id);
|
|
100
87
|
}
|
|
101
88
|
|
|
102
|
-
/// <inheritdoc />
|
|
103
89
|
public async Task<MediaResult<MediaJob>> GetAsync(MediaJobId id, CancellationToken cancellationToken = default)
|
|
104
90
|
{
|
|
105
91
|
var job = await _jobs.FindAsync(id, cancellationToken).ConfigureAwait(false);
|
|
@@ -110,16 +96,7 @@ internal sealed class MediaGenerator : IMediaGenerator
|
|
|
110
96
|
}
|
|
111
97
|
}
|
|
112
98
|
|
|
113
|
-
|
|
114
|
-
/// Executa um job: chama o provider, guarda os bytes, move o estado. <b><c>internal</c></b>
|
|
115
|
-
/// — e este e o unico tipo do subsistema que segura um <see cref="IMediaProvider"/>.
|
|
116
|
-
/// </summary>
|
|
117
|
-
/// <remarks>
|
|
118
|
-
/// A ordem tambem e o contrato: <c>Generating</c> antes da chamada (para que um worker
|
|
119
|
-
/// morto no meio deixe rastro), os bytes GRAVADOS antes de <c>Generated</c> (um job em
|
|
120
|
-
/// <c>Generated</c> sem bytes no storage mente sobre o proprio estado), e o bloqueio de
|
|
121
|
-
/// moderacao indo para <c>RejectedByPolicy</c> e nao para <c>Failed</c>.
|
|
122
|
-
/// </remarks>
|
|
99
|
+
// Generating antes da chamada (worker morto deixa rastro) e bytes gravados antes de Generated.
|
|
123
100
|
internal sealed class MediaWorker : IMediaJobRunner
|
|
124
101
|
{
|
|
125
102
|
private readonly IMediaAliasResolver _aliases;
|
|
@@ -148,7 +125,6 @@ internal sealed class MediaWorker : IMediaJobRunner
|
|
|
148
125
|
_clock = clock;
|
|
149
126
|
}
|
|
150
127
|
|
|
151
|
-
/// <inheritdoc />
|
|
152
128
|
public async Task<MediaResult<MediaJob>> RunAsync(MediaJobId id, CancellationToken cancellationToken = default)
|
|
153
129
|
{
|
|
154
130
|
var job = await _jobs.FindAsync(id, cancellationToken).ConfigureAwait(false);
|
|
@@ -186,8 +162,6 @@ internal sealed class MediaWorker : IMediaJobRunner
|
|
|
186
162
|
|
|
187
163
|
if (!produced.IsSuccess)
|
|
188
164
|
{
|
|
189
|
-
// A distincao que vale dinheiro: bloqueio de politica e terminal, falha tecnica
|
|
190
|
-
// e retryable. Colapsar as duas em "erro" e como um laco de retry vira fatura.
|
|
191
165
|
var transition = produced.Error!.Code == MediaErrorCode.PolicyBlocked
|
|
192
166
|
? job.RejectByPolicy(produced.Error.Message)
|
|
193
167
|
: job.Fail(produced.Error.Message);
|
|
@@ -232,13 +206,8 @@ internal sealed class MediaWorker : IMediaJobRunner
|
|
|
232
206
|
}
|
|
233
207
|
}
|
|
234
208
|
|
|
235
|
-
/// <summary>
|
|
236
|
-
/// Monta os adaptadores de provider. <b><c>internal</c></b>, e e por isso que nao existe
|
|
237
|
-
/// caminho publico ate um <see cref="IMediaProvider"/>.
|
|
238
|
-
/// </summary>
|
|
239
209
|
internal static class MediaProviderCatalog
|
|
240
210
|
{
|
|
241
|
-
/// <summary>Os tres adaptadores que o kit traz.</summary>
|
|
242
211
|
public static IReadOnlyList<IMediaProvider> Build(IMediaClientFactory clients)
|
|
243
212
|
{
|
|
244
213
|
ArgumentNullException.ThrowIfNull(clients);
|
|
@@ -3,53 +3,13 @@ using System.Text;
|
|
|
3
3
|
|
|
4
4
|
namespace Morph.AiKit.Media;
|
|
5
5
|
|
|
6
|
-
/// <summary>
|
|
7
|
-
/// A chave de idempotencia do <b>PEDIDO</b> — a que dedupe a CHAMADA PAGA.
|
|
8
|
-
/// </summary>
|
|
9
|
-
/// <remarks>
|
|
10
|
-
/// <para>
|
|
11
|
-
/// <b>Ela nao e o <see cref="MediaHash"/>, e a diferenca e o defeito caro deste
|
|
12
|
-
/// subsistema.</b> Esta chave e o sha256 do PEDIDO (provider + modelo + prompt normalizado
|
|
13
|
-
/// + sha256 de cada referencia + opcoes) e responde "ja pagamos por isto?". O
|
|
14
|
-
/// <see cref="MediaHash"/> e o sha256 dos BYTES e responde "ja guardamos isto?". Duas
|
|
15
|
-
/// perguntas diferentes: dois pedidos distintos podem produzir bytes identicos, e o mesmo
|
|
16
|
-
/// pedido sempre produz o mesmo pedido.
|
|
17
|
-
/// </para>
|
|
18
|
-
/// <para>
|
|
19
|
-
/// Ela e consultada <b>antes</b> de chamar o provider. E o que faz um retry — de fila, de
|
|
20
|
-
/// worker reiniciado, de webhook duplicado — custar zero.
|
|
21
|
-
/// </para>
|
|
22
|
-
/// <para>
|
|
23
|
-
/// <b>O prompt e normalizado, nao minusculado.</b> Espacos em branco colapsam e as pontas
|
|
24
|
-
/// sao aparadas, porque um espaco a mais nao muda o pedido; a caixa e preservada, porque
|
|
25
|
-
/// num prompt ela muda (nomes de marca, texto literal entre aspas que vai aparecer na
|
|
26
|
-
/// imagem).
|
|
27
|
-
/// </para>
|
|
28
|
-
/// </remarks>
|
|
29
|
-
/// <param name="Value">sha256 em hex minusculo.</param>
|
|
30
6
|
public readonly record struct MediaIdempotencyKey(string Value)
|
|
31
7
|
{
|
|
32
|
-
|
|
33
|
-
/// <remarks>
|
|
34
|
-
/// Um caractere de controle, e nao <c>|</c> ou <c>:</c>, porque o separador nao pode
|
|
35
|
-
/// aparecer dentro de um campo. Um prompt que contivesse o separador emendaria dois
|
|
36
|
-
/// campos numa chave so, e duas requisicoes diferentes passariam a compartilhar a
|
|
37
|
-
/// chave — o que faz uma reusar o artefato PAGO da outra. Pior que pagar duas vezes.
|
|
38
|
-
/// Escrito como <c>(char)0x1F</c> e nao como o caractere cru: um byte de controle
|
|
39
|
-
/// invisivel no fonte e uma armadilha para o proximo editor.
|
|
40
|
-
/// </remarks>
|
|
8
|
+
// Caractere de controle: um separador que aparecesse no prompt emendaria campos e duas requisicoes dividiriam a chave.
|
|
41
9
|
private const char FieldSeparator = (char)0x1F;
|
|
42
10
|
|
|
43
|
-
/// <summary>Separador de ITEM dentro de um campo: RECORD SEPARATOR (U+001E).</summary>
|
|
44
11
|
private const char ItemSeparator = (char)0x1E;
|
|
45
12
|
|
|
46
|
-
/// <summary>
|
|
47
|
-
/// Deriva a chave do pedido resolvido.
|
|
48
|
-
/// </summary>
|
|
49
|
-
/// <param name="request">O pedido.</param>
|
|
50
|
-
/// <param name="provider">A familia do SDK que vai atender (<c>openai</c>, <c>google</c>).</param>
|
|
51
|
-
/// <param name="model">O modelo resolvido pelo alias — nunca o alias, que pode ser repontado.</param>
|
|
52
|
-
/// <exception cref="ArgumentNullException">Pedido nulo.</exception>
|
|
53
13
|
public static MediaIdempotencyKey From(MediaRequest request, string provider, string model)
|
|
54
14
|
{
|
|
55
15
|
ArgumentNullException.ThrowIfNull(request);
|
|
@@ -70,7 +30,6 @@ public readonly record struct MediaIdempotencyKey(string Value)
|
|
|
70
30
|
Convert.ToHexStringLower(SHA256.HashData(Encoding.UTF8.GetBytes(canonical))));
|
|
71
31
|
}
|
|
72
32
|
|
|
73
|
-
/// <summary>Apara as pontas e colapsa espacos em branco. NAO minuscula.</summary>
|
|
74
33
|
public static string NormalizePrompt(string prompt)
|
|
75
34
|
{
|
|
76
35
|
ArgumentNullException.ThrowIfNull(prompt);
|
|
@@ -98,6 +57,5 @@ public readonly record struct MediaIdempotencyKey(string Value)
|
|
|
98
57
|
return builder.ToString();
|
|
99
58
|
}
|
|
100
59
|
|
|
101
|
-
/// <summary>A chave, para log.</summary>
|
|
102
60
|
public override string ToString() => Value;
|
|
103
61
|
}
|
|
@@ -1,34 +1,5 @@
|
|
|
1
1
|
namespace Morph.AiKit.Media;
|
|
2
2
|
|
|
3
|
-
/// <summary>
|
|
4
|
-
/// Um pedido de midia com estado. <b>E o unico objeto de dominio que a superficie publica
|
|
5
|
-
/// devolve</b> — nunca bytes.
|
|
6
|
-
/// </summary>
|
|
7
|
-
/// <remarks>
|
|
8
|
-
/// <para>
|
|
9
|
-
/// As transicoes sao metodos que devolvem <see cref="MediaResult{T}"/> e um job NOVO: o
|
|
10
|
-
/// registro e imutavel, e uma transicao invalida e informacao devolvida, nunca excecao.
|
|
11
|
-
/// </para>
|
|
12
|
-
/// <para>
|
|
13
|
-
/// <b>Duas invariantes carregam o standard inteiro:</b>
|
|
14
|
-
/// </para>
|
|
15
|
-
/// <list type="number">
|
|
16
|
-
/// <item>
|
|
17
|
-
/// <see cref="Approve"/> e a UNICA porta para <see cref="MediaJobState.Approved"/>, e
|
|
18
|
-
/// ela exige um revisor com nome e carimbo de tempo. E assim que "ha revisao humana"
|
|
19
|
-
/// deixa de ser uma frase no README e vira um caminho que o codigo nao tem.
|
|
20
|
-
/// </item>
|
|
21
|
-
/// <item>
|
|
22
|
-
/// <see cref="Publish"/> falha a partir de qualquer estado que nao seja
|
|
23
|
-
/// <c>Approved</c>. Nao ha atalho, nem flag, nem sobrecarga "force".
|
|
24
|
-
/// </item>
|
|
25
|
-
/// </list>
|
|
26
|
-
/// <para>
|
|
27
|
-
/// E ha uma terceira, que vale dinheiro: <see cref="RejectByPolicy"/> produz um estado
|
|
28
|
-
/// terminal com <see cref="IsRetryable"/> falso. Bloqueio de moderacao nao e erro tecnico —
|
|
29
|
-
/// retenta-lo gasta para receber o mesmo "nao".
|
|
30
|
-
/// </para>
|
|
31
|
-
/// </remarks>
|
|
32
3
|
public sealed record MediaJob
|
|
33
4
|
{
|
|
34
5
|
private MediaJob(MediaJobId id, MediaRequest request, MediaIdempotencyKey key, DateTimeOffset createdAt)
|
|
@@ -40,70 +11,44 @@ public sealed record MediaJob
|
|
|
40
11
|
State = MediaJobState.Pending;
|
|
41
12
|
}
|
|
42
13
|
|
|
43
|
-
/// <summary>A identidade. O unico valor que sai pela superficie publica.</summary>
|
|
44
14
|
public MediaJobId Id { get; private init; }
|
|
45
15
|
|
|
46
|
-
/// <summary>O pedido, congelado.</summary>
|
|
47
16
|
public MediaRequest Request { get; private init; }
|
|
48
17
|
|
|
49
|
-
/// <summary>A chave que dedupe a chamada paga.</summary>
|
|
50
18
|
public MediaIdempotencyKey IdempotencyKey { get; private init; }
|
|
51
19
|
|
|
52
|
-
/// <summary>Onde o job esta.</summary>
|
|
53
20
|
public MediaJobState State { get; private init; }
|
|
54
21
|
|
|
55
|
-
/// <summary>Quando o pedido foi aceito.</summary>
|
|
56
22
|
public DateTimeOffset CreatedAt { get; private init; }
|
|
57
23
|
|
|
58
|
-
/// <summary>sha256 dos bytes, a partir de <see cref="MediaJobState.Generated"/>.</summary>
|
|
59
24
|
public MediaHash? Hash { get; private init; }
|
|
60
25
|
|
|
61
|
-
/// <summary>Caminho RELATIVO no blob do projeto. Nunca URL de provider.</summary>
|
|
62
26
|
public string? RelativePath { get; private init; }
|
|
63
27
|
|
|
64
|
-
/// <summary>A proveniencia, gravada na geracao.</summary>
|
|
65
28
|
public MediaProvenance? Provenance { get; private init; }
|
|
66
29
|
|
|
67
|
-
/// <summary>Quem decidiu, quando, e o que escreveu. Nulo enquanto ninguem revisou.</summary>
|
|
68
30
|
public MediaReview? Review { get; private init; }
|
|
69
31
|
|
|
70
|
-
/// <summary>Por que falhou ou foi bloqueado. Nulo nos estados saudaveis.</summary>
|
|
71
32
|
public string? FailureReason { get; private init; }
|
|
72
33
|
|
|
73
|
-
/// <summary>Quando foi publicado.</summary>
|
|
74
34
|
public DateTimeOffset? PublishedAt { get; private init; }
|
|
75
35
|
|
|
76
|
-
/// <summary>
|
|
77
|
-
/// A marcação "gerado por IA" registrada na publicação, ou nulo quando a finalidade não
|
|
78
|
-
/// a exige. Ver <see cref="MediaDisclosure"/> e <see cref="Publish"/>.
|
|
79
|
-
/// </summary>
|
|
80
36
|
public MediaDisclosure? Disclosure { get; private init; }
|
|
81
37
|
|
|
82
|
-
|
|
83
|
-
/// Se vale tentar de novo. <b>So <see cref="MediaJobState.Failed"/> e retryable.</b>
|
|
84
|
-
/// <see cref="MediaJobState.RejectedByPolicy"/> nao e, e essa e a diferenca que impede
|
|
85
|
-
/// um laco de retry de virar uma fatura.
|
|
86
|
-
/// </summary>
|
|
38
|
+
// RejectedByPolicy nao e retryable: retentar um bloqueio de moderacao paga pelo mesmo "nao".
|
|
87
39
|
public bool IsRetryable => State == MediaJobState.Failed;
|
|
88
40
|
|
|
89
|
-
/// <summary>Nasce em <see cref="MediaJobState.Pending"/>. Nada foi gasto ainda.</summary>
|
|
90
|
-
/// <exception cref="ArgumentNullException">Pedido nulo.</exception>
|
|
91
41
|
public static MediaJob Create(MediaRequest request, MediaIdempotencyKey key, DateTimeOffset createdAt)
|
|
92
42
|
{
|
|
93
43
|
ArgumentNullException.ThrowIfNull(request);
|
|
94
44
|
return new MediaJob(MediaJobId.New(), request, key, createdAt);
|
|
95
45
|
}
|
|
96
46
|
|
|
97
|
-
/// <summary>Pending para Generating: a chamada paga esta em voo.</summary>
|
|
98
47
|
public MediaResult<MediaJob> StartGenerating()
|
|
99
48
|
=> State == MediaJobState.Pending || State == MediaJobState.Failed
|
|
100
49
|
? MediaResult<MediaJob>.Ok(this with { State = MediaJobState.Generating, FailureReason = null })
|
|
101
50
|
: Invalid(nameof(StartGenerating), MediaJobState.Generating);
|
|
102
51
|
|
|
103
|
-
/// <summary>
|
|
104
|
-
/// Generating para Generated. Exige hash, caminho e proveniencia <b>juntos</b>: um job
|
|
105
|
-
/// em <c>Generated</c> sem bytes no seu storage esta mentindo sobre o proprio estado.
|
|
106
|
-
/// </summary>
|
|
107
52
|
public MediaResult<MediaJob> MarkGenerated(MediaHash hash, string relativePath, MediaProvenance provenance)
|
|
108
53
|
{
|
|
109
54
|
ArgumentException.ThrowIfNullOrWhiteSpace(relativePath);
|
|
@@ -120,7 +65,6 @@ public sealed record MediaJob
|
|
|
120
65
|
: Invalid(nameof(MarkGenerated), MediaJobState.Generated);
|
|
121
66
|
}
|
|
122
67
|
|
|
123
|
-
/// <summary>Falha tecnica. Retryable.</summary>
|
|
124
68
|
public MediaResult<MediaJob> Fail(string reason)
|
|
125
69
|
{
|
|
126
70
|
ArgumentException.ThrowIfNullOrWhiteSpace(reason);
|
|
@@ -130,9 +74,6 @@ public sealed record MediaJob
|
|
|
130
74
|
: Invalid(nameof(Fail), MediaJobState.Failed);
|
|
131
75
|
}
|
|
132
76
|
|
|
133
|
-
/// <summary>
|
|
134
|
-
/// Bloqueio de moderacao. Terminal e NAO retryable — ver <see cref="IsRetryable"/>.
|
|
135
|
-
/// </summary>
|
|
136
77
|
public MediaResult<MediaJob> RejectByPolicy(string reason)
|
|
137
78
|
{
|
|
138
79
|
ArgumentException.ThrowIfNullOrWhiteSpace(reason);
|
|
@@ -146,10 +87,6 @@ public sealed record MediaJob
|
|
|
146
87
|
: Invalid(nameof(RejectByPolicy), MediaJobState.RejectedByPolicy);
|
|
147
88
|
}
|
|
148
89
|
|
|
149
|
-
/// <summary>
|
|
150
|
-
/// A UNICA porta para <see cref="MediaJobState.Approved"/>, e ela exige um humano
|
|
151
|
-
/// nomeado com carimbo de tempo.
|
|
152
|
-
/// </summary>
|
|
153
90
|
public MediaResult<MediaJob> Approve(MediaReview review)
|
|
154
91
|
{
|
|
155
92
|
ArgumentNullException.ThrowIfNull(review);
|
|
@@ -167,7 +104,6 @@ public sealed record MediaJob
|
|
|
167
104
|
: Invalid(nameof(Approve), MediaJobState.Approved);
|
|
168
105
|
}
|
|
169
106
|
|
|
170
|
-
/// <summary>Recusa humana. Terminal.</summary>
|
|
171
107
|
public MediaResult<MediaJob> Reject(MediaReview review)
|
|
172
108
|
{
|
|
173
109
|
ArgumentNullException.ThrowIfNull(review);
|
|
@@ -184,37 +120,6 @@ public sealed record MediaJob
|
|
|
184
120
|
: Invalid(nameof(Reject), MediaJobState.Rejected);
|
|
185
121
|
}
|
|
186
122
|
|
|
187
|
-
/// <summary>
|
|
188
|
-
/// Publica. <b>Tres portas, e cada uma fecha um caminho que existia so na prosa:</b>
|
|
189
|
-
/// </summary>
|
|
190
|
-
/// <remarks>
|
|
191
|
-
/// <list type="number">
|
|
192
|
-
/// <item>
|
|
193
|
-
/// Falha a partir de qualquer estado que nao seja <see cref="MediaJobState.Approved"/>
|
|
194
|
-
/// — inclusive de <c>Generated</c>, que e o atalho que alguem sempre quer. E o que
|
|
195
|
-
/// torna a REVISAO HUMANA um caminho que o codigo nao tem.
|
|
196
|
-
/// </item>
|
|
197
|
-
/// <item>
|
|
198
|
-
/// Falha quando a finalidade nao e publicavel. <see cref="MediaPurpose.Draft"/> se
|
|
199
|
-
/// declarava "nunca publicavel" num comentario XML que nada cobrava.
|
|
200
|
-
/// </item>
|
|
201
|
-
/// <item>
|
|
202
|
-
/// Falha quando a finalidade exige MARCACAO e ela nao foi passada. Era a unica das
|
|
203
|
-
/// quatro afirmacoes da rubrica <c>media-claims</c> presa por prosa — e e a que
|
|
204
|
-
/// carrega a obrigacao legal (AI Act art. 50).
|
|
205
|
-
/// </item>
|
|
206
|
-
/// </list>
|
|
207
|
-
/// <para>
|
|
208
|
-
/// O que isto prova e o que nao prova esta escrito em <see cref="MediaDisclosure"/>: o
|
|
209
|
-
/// kit garante que a DECISAO foi tomada e gravada, nao que o rotulo chegou aos olhos de
|
|
210
|
-
/// alguem — a renderizacao e do aplicativo consumidor.
|
|
211
|
-
/// </para>
|
|
212
|
-
/// </remarks>
|
|
213
|
-
/// <param name="disclosure">
|
|
214
|
-
/// A marcacao. Pode ser nula somente quando <see cref="MediaPurpose.RequiresDisclosure"/>
|
|
215
|
-
/// e falso para a finalidade do pedido.
|
|
216
|
-
/// </param>
|
|
217
|
-
/// <param name="publishedAt">Quando.</param>
|
|
218
123
|
public MediaResult<MediaJob> Publish(MediaDisclosure? disclosure, DateTimeOffset publishedAt)
|
|
219
124
|
{
|
|
220
125
|
if (State != MediaJobState.Approved)
|
|
@@ -1,21 +1,8 @@
|
|
|
1
1
|
namespace Morph.AiKit.Media;
|
|
2
2
|
|
|
3
|
-
/// <summary>
|
|
4
|
-
/// A identidade de um job de midia. <b>O unico valor que a superficie publica devolve</b> —
|
|
5
|
-
/// nunca bytes.
|
|
6
|
-
/// </summary>
|
|
7
|
-
/// <remarks>
|
|
8
|
-
/// <c>Guid.CreateVersion7()</c> e nao <c>Guid.NewGuid()</c>: v7 e ordenavel por tempo, o
|
|
9
|
-
/// que mantem o indice do banco do projeto sequencial em vez de espalhar escrita por toda
|
|
10
|
-
/// a arvore. E a regra da casa, escrita aqui porque este e o unico lugar do subsistema que
|
|
11
|
-
/// gera identidade.
|
|
12
|
-
/// </remarks>
|
|
13
|
-
/// <param name="Value">O GUID v7.</param>
|
|
14
3
|
public readonly record struct MediaJobId(Guid Value)
|
|
15
4
|
{
|
|
16
|
-
/// <summary>Uma identidade nova, ordenavel por tempo.</summary>
|
|
17
5
|
public static MediaJobId New() => new(Guid.CreateVersion7());
|
|
18
6
|
|
|
19
|
-
/// <summary>Forma compacta, para log e URL.</summary>
|
|
20
7
|
public override string ToString() => Value.ToString("N");
|
|
21
8
|
}
|
|
@@ -1,47 +1,20 @@
|
|
|
1
1
|
namespace Morph.AiKit.Media;
|
|
2
2
|
|
|
3
|
-
/// <summary>
|
|
4
|
-
/// Os oito estados de um job de midia.
|
|
5
|
-
/// </summary>
|
|
6
|
-
/// <remarks>
|
|
7
|
-
/// <para>
|
|
8
|
-
/// A distincao que vale dinheiro esta entre <see cref="Failed"/> e
|
|
9
|
-
/// <see cref="RejectedByPolicy"/>. Um bloqueio de moderacao e uma resposta DEFINITIVA:
|
|
10
|
-
/// retenta-lo gasta para receber o mesmo "nao" — e no provider que cobra o bloqueio, gasta
|
|
11
|
-
/// duas vezes. Modelar bloqueio como erro tecnico e como um laco de retry vira uma fatura.
|
|
12
|
-
/// </para>
|
|
13
|
-
/// <para>
|
|
14
|
-
/// A distincao que vale credibilidade esta entre <see cref="Generated"/> e
|
|
15
|
-
/// <see cref="Approved"/>: <c>Approved</c> so existe por uma transicao que carimba um
|
|
16
|
-
/// revisor humano, e <c>Published</c> so existe a partir de <c>Approved</c>.
|
|
17
|
-
/// </para>
|
|
18
|
-
/// </remarks>
|
|
19
3
|
public enum MediaJobState
|
|
20
4
|
{
|
|
21
|
-
/// <summary>Pedido aceito. Nada foi gasto ainda.</summary>
|
|
22
5
|
Pending = 0,
|
|
23
6
|
|
|
24
|
-
/// <summary>Chamada paga em voo, ou tarefa remota em polling.</summary>
|
|
25
7
|
Generating = 1,
|
|
26
8
|
|
|
27
|
-
/// <summary>Os bytes existem NO SEU storage. Nao basta a tarefa remota ter concluido.</summary>
|
|
28
9
|
Generated = 2,
|
|
29
10
|
|
|
30
|
-
/// <summary>Um humano aprovou, com nome e carimbo de tempo. Unica porta para <see cref="Published"/>.</summary>
|
|
31
11
|
Approved = 3,
|
|
32
12
|
|
|
33
|
-
/// <summary>Um humano recusou. Terminal.</summary>
|
|
34
13
|
Rejected = 4,
|
|
35
14
|
|
|
36
|
-
/// <summary>Bloqueio de moderacao do provider. Terminal e <b>nao</b> retryable.</summary>
|
|
37
15
|
RejectedByPolicy = 5,
|
|
38
16
|
|
|
39
|
-
/// <summary>
|
|
40
|
-
/// Publicado. So se alcanca por <see cref="MediaJob.Publish"/>, que exige finalidade
|
|
41
|
-
/// publicavel e — quando a finalidade a pede — uma <see cref="MediaDisclosure"/> registrada.
|
|
42
|
-
/// </summary>
|
|
43
17
|
Published = 6,
|
|
44
18
|
|
|
45
|
-
/// <summary>Erro tecnico (429, 5xx, timeout, download quebrado). Retryable.</summary>
|
|
46
19
|
Failed = 7,
|
|
47
20
|
}
|
|
@@ -1,23 +1,5 @@
|
|
|
1
1
|
namespace Morph.AiKit.Media;
|
|
2
2
|
|
|
3
|
-
/// <summary>
|
|
4
|
-
/// Tarifa de midia.
|
|
5
|
-
/// </summary>
|
|
6
|
-
/// <remarks>
|
|
7
|
-
/// <para>
|
|
8
|
-
/// <b>Nao e o <c>ModelPricing</c> do registry, e a diferenca e a UNIDADE.</b>
|
|
9
|
-
/// <c>ModelPricing</c> cobra por 1M de tokens; imagem cobra por imagem e por qualidade, e
|
|
10
|
-
/// video cobra por segundo de saida. Espremer as tres unidades na mesma tarifa produz custo
|
|
11
|
-
/// errado em silencio — que e a pior forma de custo errado, porque nada grita.
|
|
12
|
-
/// </para>
|
|
13
|
-
/// <para>
|
|
14
|
-
/// Os numeros NAO moram aqui. Eles moram nas tabelas datadas de
|
|
15
|
-
/// <c>ai-agents/modalities-image-gen.md</c> (secao "Custo por chamada") e
|
|
16
|
-
/// <c>ai-agents/media-video.md</c> (secao "Custo por segundo"), e sao carregados do
|
|
17
|
-
/// <c>model-registry.v2.json</c> do projeto. Um preco chumbado em codigo envelhece sem que
|
|
18
|
-
/// ninguem reveja o standard.
|
|
19
|
-
/// </para>
|
|
20
|
-
/// </remarks>
|
|
21
3
|
public sealed record MediaPricing
|
|
22
4
|
{
|
|
23
5
|
private readonly decimal _low;
|
|
@@ -34,11 +16,8 @@ public sealed record MediaPricing
|
|
|
34
16
|
_perSecond = perSecond;
|
|
35
17
|
}
|
|
36
18
|
|
|
37
|
-
/// <summary>A que tipo de midia esta tarifa se aplica.</summary>
|
|
38
19
|
public MediaKind Kind { get; }
|
|
39
20
|
|
|
40
|
-
/// <summary>Tarifa de imagem, em USD por imagem, por faixa de qualidade.</summary>
|
|
41
|
-
/// <exception cref="ArgumentOutOfRangeException">Tarifa negativa.</exception>
|
|
42
21
|
public static MediaPricing PerImage(decimal low, decimal medium, decimal high)
|
|
43
22
|
{
|
|
44
23
|
ArgumentOutOfRangeException.ThrowIfNegative(low);
|
|
@@ -48,8 +27,6 @@ public sealed record MediaPricing
|
|
|
48
27
|
return new MediaPricing(MediaKind.Image, low, medium, high, 0m);
|
|
49
28
|
}
|
|
50
29
|
|
|
51
|
-
/// <summary>Tarifa de video, em USD por segundo de saida.</summary>
|
|
52
|
-
/// <exception cref="ArgumentOutOfRangeException">Tarifa negativa.</exception>
|
|
53
30
|
public static MediaPricing PerSecond(decimal usdPerSecond)
|
|
54
31
|
{
|
|
55
32
|
ArgumentOutOfRangeException.ThrowIfNegative(usdPerSecond);
|
|
@@ -57,16 +34,6 @@ public sealed record MediaPricing
|
|
|
57
34
|
return new MediaPricing(MediaKind.Video, 0m, 0m, 0m, usdPerSecond);
|
|
58
35
|
}
|
|
59
36
|
|
|
60
|
-
/// <summary>
|
|
61
|
-
/// Custo estimado do pedido, <b>antes</b> da chamada. E este numero que o teto por
|
|
62
|
-
/// tenant/dia confronta e que a proveniencia grava: estimar depois e contabilidade,
|
|
63
|
-
/// estimar antes e controle.
|
|
64
|
-
/// </summary>
|
|
65
|
-
/// <remarks>
|
|
66
|
-
/// Qualidade ausente conta como <see cref="MediaQuality.Low"/> porque a escada de custo
|
|
67
|
-
/// da casa comeca em <c>low</c>; duracao ausente num pedido de video e recusa, e nao um
|
|
68
|
-
/// palpite — arbitrar segundos e arbitrar dinheiro.
|
|
69
|
-
/// </remarks>
|
|
70
37
|
public MediaResult<decimal> EstimateUsd(MediaRequest request)
|
|
71
38
|
{
|
|
72
39
|
ArgumentNullException.ThrowIfNull(request);
|
|
@@ -1,36 +1,5 @@
|
|
|
1
1
|
namespace Morph.AiKit.Media;
|
|
2
2
|
|
|
3
|
-
/// <summary>
|
|
4
|
-
/// A proveniencia de uma midia gerada: o que foi pedido, a quem, com que opcoes, por quanto.
|
|
5
|
-
/// </summary>
|
|
6
|
-
/// <remarks>
|
|
7
|
-
/// <para>
|
|
8
|
-
/// Gravada no momento da geracao, uma por job. Sem ela nao existe auditoria de "que modelo
|
|
9
|
-
/// gerou isto", nao existe reproducao do pedido, e nao existe base para responder a uma
|
|
10
|
-
/// reclamacao sobre o que a imagem mostra.
|
|
11
|
-
/// </para>
|
|
12
|
-
/// <para>
|
|
13
|
-
/// <see cref="RevisedPrompt"/> merece o campo proprio porque o provider frequentemente
|
|
14
|
-
/// reescreve o prompt antes de gerar — guardar so o enviado esconde metade do que
|
|
15
|
-
/// aconteceu.
|
|
16
|
-
/// </para>
|
|
17
|
-
/// <para>
|
|
18
|
-
/// <see cref="C2paPreserved"/> nao e enfeite: os metadados C2PA sao a unica prova tecnica
|
|
19
|
-
/// de origem que a imagem carrega, e um resize "para economizar CDN" os apaga. O campo
|
|
20
|
-
/// registra se o SEU pipeline os manteve. E ele nao substitui a marcacao visivel: C2PA cai
|
|
21
|
-
/// com screenshot e re-encode, e <c>not_detected</c> nunca significa "feito por humano".
|
|
22
|
-
/// </para>
|
|
23
|
-
/// </remarks>
|
|
24
|
-
/// <param name="Provider">Familia do SDK: <c>openai</c>, <c>google</c>.</param>
|
|
25
|
-
/// <param name="ModelId">Id do modelo no provider — o resolvido pelo alias, nunca o alias.</param>
|
|
26
|
-
/// <param name="PromptFinal">O texto efetivamente enviado, ja montado pelo template.</param>
|
|
27
|
-
/// <param name="RevisedPrompt">O que o provider reescreveu, ou nulo quando ele nao devolve.</param>
|
|
28
|
-
/// <param name="RefsHash">sha256 das referencias, na mesma ordem da chave de pedido.</param>
|
|
29
|
-
/// <param name="Options">Forma canonica das opcoes enviadas.</param>
|
|
30
|
-
/// <param name="ResponseId">Id do lado do provider, para abrir chamado.</param>
|
|
31
|
-
/// <param name="EstimatedCostUsd">Custo estimado <b>antes</b> da chamada.</param>
|
|
32
|
-
/// <param name="CreatedAt">Quando.</param>
|
|
33
|
-
/// <param name="C2paPreserved">Se os metadados de proveniencia sobreviveram ao pipeline.</param>
|
|
34
3
|
public sealed record MediaProvenance(
|
|
35
4
|
string Provider,
|
|
36
5
|
string ModelId,
|