@polymorphism-tech/morph-spec 8.33.0 → 8.35.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 +22 -2
- package/README.md +2 -2
- package/bin/morph-spec.js +43 -5
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +5 -4
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +2 -2
- package/framework/MORPH.md +20 -22
- package/framework/agents.json +198 -44
- package/framework/ai-pin.json +22 -0
- package/framework/commands/morph-media.md +45 -0
- package/framework/docs/CLI.md +238 -10
- package/framework/evals/proof-rubrics/README.md +76 -0
- package/framework/evals/proof-rubrics/eval-de-modelo.md +64 -0
- package/framework/evals/proof-rubrics/fiacao.md +54 -0
- package/framework/evals/proof-rubrics/guard-cego.md +52 -0
- package/framework/evals/proof-rubrics/media-claims.md +109 -0
- package/framework/evals/proof-rubrics/mutation-proof.md +77 -0
- package/framework/evals/proof-rubrics/observabilidade-genai.md +119 -0
- package/framework/evals/proof-rubrics/verde-nao-e-coberto.md +48 -0
- package/framework/evals/rubrics/architecture.md +30 -0
- package/framework/evals/rubrics/code-quality.md +26 -0
- package/framework/evals/rubrics/test-coverage.md +20 -0
- package/framework/hooks/README.md +11 -1
- package/framework/hooks/claude-code/core/block-destructive-worktree-removal.js +5 -8
- package/framework/hooks/claude-code/core/block-raw-worktree-add.js +79 -0
- package/framework/hooks/claude-code/core/chain.js +27 -7
- package/framework/hooks/claude-code/core/protect-spec-files.js +41 -4
- package/framework/hooks/claude-code/core/worktree-context.js +83 -14
- package/framework/hooks/claude-code/core/worktree-create.js +206 -0
- package/framework/hooks/claude-code/core/worktree-env-inject.js +87 -0
- package/framework/hooks/claude-code/core/worktree-remove.js +81 -0
- package/framework/hooks/shared/command-tokens.js +23 -0
- package/framework/hooks/shared/e2e-check.js +0 -0
- package/framework/hooks/shared/git-paths.js +25 -0
- package/framework/hooks/shared/hook-response.js +17 -0
- package/framework/hooks/shared/morph-cli.js +134 -0
- package/framework/rules/ai-agents-standards.md +14 -2
- package/framework/rules/infrastructure-standards.md +3 -1
- package/framework/schemas/ai-pin.schema.json +97 -0
- package/framework/schemas/config.schema.json +107 -0
- package/framework/schemas/feature.schema.json +23 -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 +28 -7
- 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 +20 -2
- package/framework/skills/core/morph-media/SKILL.md +215 -0
- package/framework/skills/core/morph-plan/SKILL.md +38 -4
- package/framework/skills/core/morph-proposal/SKILL.md +1 -1
- package/framework/skills/core/morph-review/SKILL.md +166 -9
- 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 +1 -1
- package/framework/standards/PROVENANCE.json +81 -3
- package/framework/standards/STANDARDS.json +1504 -830
- package/framework/standards/ai-agents/agent-archetypes.md +160 -0
- package/framework/standards/ai-agents/agent-session.md +256 -160
- package/framework/standards/ai-agents/agent-spec.md +196 -0
- package/framework/standards/ai-agents/batching.md +217 -193
- package/framework/standards/ai-agents/context-providers.md +261 -188
- package/framework/standards/ai-agents/conversational-agent-with-phases.md +239 -0
- package/framework/standards/ai-agents/cost-and-budget.md +256 -0
- package/framework/standards/ai-agents/durable-workflows-hitl.md +98 -5
- package/framework/standards/ai-agents/evals-with-cache.md +289 -0
- package/framework/standards/ai-agents/guardrails.md +222 -0
- package/framework/standards/ai-agents/llm-runtime-defaults.md +226 -0
- package/framework/standards/ai-agents/mcp-server.md +351 -250
- package/framework/standards/ai-agents/mcp-tools.md +250 -197
- package/framework/standards/ai-agents/media-pipeline.md +352 -0
- package/framework/standards/ai-agents/media-video.md +267 -0
- package/framework/standards/ai-agents/middleware-patterns.md +241 -207
- package/framework/standards/ai-agents/modalities-image-gen.md +422 -173
- package/framework/standards/ai-agents/modalities-speech-to-text.md +177 -171
- package/framework/standards/ai-agents/modalities-text-to-speech.md +189 -181
- package/framework/standards/ai-agents/modalities-vision.md +205 -201
- package/framework/standards/ai-agents/multi-agent-patterns.md +211 -155
- package/framework/standards/ai-agents/observability-patterns.md +227 -17
- package/framework/standards/ai-agents/production.md +260 -231
- package/framework/standards/ai-agents/prompt-sources.md +389 -0
- package/framework/standards/ai-agents/providers/model-registry.md +293 -158
- package/framework/standards/ai-agents/rag-custom-pgvector.md +69 -4
- package/framework/standards/ai-agents/service-tier-flex.md +143 -135
- package/framework/standards/ai-agents/setup.md +166 -50
- package/framework/standards/ai-agents/structured-output.md +280 -249
- package/framework/standards/ai-agents/sweet-spot.md +129 -109
- package/framework/standards/ai-agents/testing-ai.md +236 -0
- package/framework/standards/ai-agents/vector-data-extensions.md +50 -20
- package/framework/standards/ai-agents/workflows.md +27 -7
- package/framework/standards/frontend/design-system/ai-image-generation.md +12 -0
- package/framework/standards/frontend/design-system/ai-video-generation.md +13 -0
- package/framework/standards/infrastructure/docker/local-compose-isolation.md +90 -0
- package/framework/templates/REGISTRY.json +14 -1
- package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +95 -95
- package/framework/templates/code/sql/ai-agents/agent-prompts-migration.sql.template +58 -58
- package/framework/templates/dotnet/ai-kit/Directory.Build.props +25 -0
- package/framework/templates/dotnet/ai-kit/Directory.Packages.props +26 -0
- package/framework/templates/dotnet/ai-kit/Morph.AiKit.sln +56 -0
- package/framework/templates/dotnet/ai-kit/NuGet.config +12 -0
- package/framework/templates/dotnet/ai-kit/README.md +128 -0
- package/framework/templates/dotnet/ai-kit/global.json +9 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentApi.cs +65 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactory.cs +289 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactoryOptions.cs +91 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpec.cs +91 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpecOptions.cs +181 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/AiKitAssemblyMarker.cs +14 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/ConversationStore.cs +174 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/IConversationStore.cs +54 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/InMemoryConversationStore.cs +75 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/CountingChatClient.cs +71 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalAssertion.cs +246 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheMissException.cs +57 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheOptions.cs +78 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheStamp.cs +225 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalFixture.cs +463 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalMode.cs +32 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalQualityEvaluators.cs +49 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalScenario.cs +214 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalSummary.cs +168 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/OfflineChatClient.cs +142 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/ShortPathResponseCache.cs +349 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaGenerator.cs +42 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaProvider.cs +77 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaArtifact.cs +28 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaDisclosure.cs +107 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaGenerator.cs +253 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaIdempotencyKey.cs +103 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJob.cs +259 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobId.cs +21 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobState.cs +47 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaPricing.cs +102 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaProvenance.cs +44 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaRequest.cs +192 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaResult.cs +94 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaReview.cs +99 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaSeams.cs +97 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaStore.cs +133 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MorphMedia.cs +93 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/GoogleImageProvider.cs +112 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/OpenAIImageProvider.cs +288 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/VeoVideoProvider.cs +158 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/IUsageCostSink.cs +29 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostMiddleware.cs +208 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostRecord.cs +95 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Morph.AiKit.csproj +38 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphOpenTelemetryExtensions.cs +119 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphTelemetryOptions.cs +82 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/IProviderClientFactory.cs +33 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelAlias.cs +267 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelRegistry.cs +474 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/model-registry.v2.json +87 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaIngest.cs +169 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaPlan.cs +49 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentFactoryTests.cs +495 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentSpecTests.cs +260 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ConversationStoreTests.cs +405 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/DeltaIngestTests.cs +379 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalAssertionTests.cs +197 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheMissTests.cs +139 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheStampTests.cs +157 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFailuresTests.cs +178 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFixtureTests.cs +153 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalRefreshTests.cs +194 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalWorkspace.cs +86 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/KitEvals.cs +213 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/OfflineChatClientTests.cs +95 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClient.cs +225 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClientTests.cs +215 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeClock.cs +33 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeMediaProvider.cs +89 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeProviderClientFactory.cs +55 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeUsageCostSink.cs +60 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/InMemoryMediaStore.cs +131 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAccessibilityTests.cs +133 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAliasIsNotAnAgentTests.cs +127 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaDisclosureTests.cs +216 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaGeneratorTests.cs +264 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaIdempotencyKeyTests.cs +148 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaJobStateTests.cs +184 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaReviewTests.cs +89 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaStoreTests.cs +98 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ModelRegistryTests.cs +451 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Morph.AiKit.Tests.csproj +76 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/SkeletonSmokeTests.cs +33 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/TelemetryTests.cs +261 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/UsageCostMiddlewareTests.cs +374 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/.morph-eval-cache.json +12 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/orcamento.acme.a-resposta-estruturada/1/CF70A7CE27BBD66A/contents.data +13 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/orcamento.acme.a-resposta-estruturada/1/CF70A7CE27BBD66A/entry.json +7 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.a-saudacao/1/19F631228C6E00BA/contents.data +13 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.a-saudacao/1/19F631228C6E00BA/entry.json +7 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.b-escalonamento/1/A24A7C9BDD8239D8/contents.data +13 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.b-escalonamento/1/A24A7C9BDD8239D8/entry.json +7 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.globex.a-saudacao/1/E38CD37DF003EDB7/contents.data +13 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.globex.a-saudacao/1/E38CD37DF003EDB7/entry.json +7 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/orcamento/acme/a-resposta-estruturada.json +24 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/triagem/acme/a-saudacao.json +23 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/triagem/acme/b-escalonamento.json +24 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/triagem/globex/a-saudacao.json +23 -0
- package/framework/templates/infrastructure/docker/docker-compose.template.yml +24 -14
- package/package.json +3 -3
- package/src/commands/e2e/e2e.js +91 -40
- package/src/commands/project/doctor.js +143 -6
- package/src/commands/project/env.js +95 -0
- package/src/commands/project/update.js +469 -153
- package/src/commands/project/worktree.js +274 -53
- package/src/commands/state/advance.js +24 -2
- package/src/commands/state/approve.js +33 -1
- package/src/commands/state/create.js +72 -11
- package/src/commands/state/delete.js +8 -0
- package/src/commands/state/finish.js +360 -53
- package/src/commands/state/gate-check.js +190 -6
- package/src/commands/state/score.js +36 -1
- package/src/commands/validation/verify.js +134 -25
- package/src/core/state/state-manager.js +9 -1
- package/src/dashboard/app/pages/HookInspector.jsx +430 -418
- package/src/dashboard/scanner/hooks-scanner.js +5 -1
- package/src/lib/ai/ai-checks.js +182 -0
- package/src/lib/ai/ai-project-scan.js +347 -0
- package/src/lib/ai/code-audit.js +181 -0
- package/src/lib/ai/comment-stripper.js +184 -0
- package/src/lib/ai/pin-drift.js +138 -0
- package/src/lib/ai/registry-audit.js +285 -0
- package/src/lib/e2e/compose-env.js +55 -14
- package/src/lib/e2e/stack-teardown.js +115 -0
- package/src/lib/evaluator-report.js +269 -15
- package/src/lib/generators/architecture-scanner.js +2 -1
- package/src/lib/git/default-branch.js +92 -4
- package/src/lib/graph/graphifyignore.js +2 -1
- package/src/lib/installers/effects.js +193 -0
- package/src/lib/installers/templates-reconcile.js +3 -3
- package/src/lib/risk-detector.js +284 -0
- package/src/lib/standards/verified-against.js +276 -0
- package/src/lib/tasks/eval-node.js +485 -0
- package/src/lib/tasks/linux-runner.js +297 -0
- package/src/lib/tasks/test-nodes.js +486 -0
- package/src/lib/tasks/test-runner.js +431 -37
- package/src/lib/validators/verify-runner.js +590 -28
- package/src/lib/worktree/dirs.js +32 -0
- package/src/lib/worktree/docker-audit.js +287 -0
- package/src/lib/worktree/env-allocator.js +69 -11
- package/src/lib/worktree/worktree-audit.js +70 -3
- package/src/scripts/setup-infra.js +24 -11
- package/src/utils/agents-installer.js +18 -8
- package/src/utils/claude-md-injector.js +10 -5
- package/src/utils/file-copier.js +28 -16
- package/src/utils/hooks-installer.js +75 -29
- package/src/utils/skills-installer.js +194 -187
- package/src/utils/version-checker.js +68 -3
- package/framework/standards/ai-agents/prompts-in-db.md +0 -214
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
namespace Morph.AiKit.Media;
|
|
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
|
+
public enum MediaJobState
|
|
20
|
+
{
|
|
21
|
+
/// <summary>Pedido aceito. Nada foi gasto ainda.</summary>
|
|
22
|
+
Pending = 0,
|
|
23
|
+
|
|
24
|
+
/// <summary>Chamada paga em voo, ou tarefa remota em polling.</summary>
|
|
25
|
+
Generating = 1,
|
|
26
|
+
|
|
27
|
+
/// <summary>Os bytes existem NO SEU storage. Nao basta a tarefa remota ter concluido.</summary>
|
|
28
|
+
Generated = 2,
|
|
29
|
+
|
|
30
|
+
/// <summary>Um humano aprovou, com nome e carimbo de tempo. Unica porta para <see cref="Published"/>.</summary>
|
|
31
|
+
Approved = 3,
|
|
32
|
+
|
|
33
|
+
/// <summary>Um humano recusou. Terminal.</summary>
|
|
34
|
+
Rejected = 4,
|
|
35
|
+
|
|
36
|
+
/// <summary>Bloqueio de moderacao do provider. Terminal e <b>nao</b> retryable.</summary>
|
|
37
|
+
RejectedByPolicy = 5,
|
|
38
|
+
|
|
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
|
+
Published = 6,
|
|
44
|
+
|
|
45
|
+
/// <summary>Erro tecnico (429, 5xx, timeout, download quebrado). Retryable.</summary>
|
|
46
|
+
Failed = 7,
|
|
47
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
namespace Morph.AiKit.Media;
|
|
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
|
+
public sealed record MediaPricing
|
|
22
|
+
{
|
|
23
|
+
private readonly decimal _low;
|
|
24
|
+
private readonly decimal _medium;
|
|
25
|
+
private readonly decimal _high;
|
|
26
|
+
private readonly decimal _perSecond;
|
|
27
|
+
|
|
28
|
+
private MediaPricing(MediaKind kind, decimal low, decimal medium, decimal high, decimal perSecond)
|
|
29
|
+
{
|
|
30
|
+
Kind = kind;
|
|
31
|
+
_low = low;
|
|
32
|
+
_medium = medium;
|
|
33
|
+
_high = high;
|
|
34
|
+
_perSecond = perSecond;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/// <summary>A que tipo de midia esta tarifa se aplica.</summary>
|
|
38
|
+
public MediaKind Kind { get; }
|
|
39
|
+
|
|
40
|
+
/// <summary>Tarifa de imagem, em USD por imagem, por faixa de qualidade.</summary>
|
|
41
|
+
/// <exception cref="ArgumentOutOfRangeException">Tarifa negativa.</exception>
|
|
42
|
+
public static MediaPricing PerImage(decimal low, decimal medium, decimal high)
|
|
43
|
+
{
|
|
44
|
+
ArgumentOutOfRangeException.ThrowIfNegative(low);
|
|
45
|
+
ArgumentOutOfRangeException.ThrowIfNegative(medium);
|
|
46
|
+
ArgumentOutOfRangeException.ThrowIfNegative(high);
|
|
47
|
+
|
|
48
|
+
return new MediaPricing(MediaKind.Image, low, medium, high, 0m);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/// <summary>Tarifa de video, em USD por segundo de saida.</summary>
|
|
52
|
+
/// <exception cref="ArgumentOutOfRangeException">Tarifa negativa.</exception>
|
|
53
|
+
public static MediaPricing PerSecond(decimal usdPerSecond)
|
|
54
|
+
{
|
|
55
|
+
ArgumentOutOfRangeException.ThrowIfNegative(usdPerSecond);
|
|
56
|
+
|
|
57
|
+
return new MediaPricing(MediaKind.Video, 0m, 0m, 0m, usdPerSecond);
|
|
58
|
+
}
|
|
59
|
+
|
|
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
|
+
public MediaResult<decimal> EstimateUsd(MediaRequest request)
|
|
71
|
+
{
|
|
72
|
+
ArgumentNullException.ThrowIfNull(request);
|
|
73
|
+
|
|
74
|
+
if (request.Kind != Kind)
|
|
75
|
+
{
|
|
76
|
+
return MediaResult<decimal>.Fail(
|
|
77
|
+
MediaErrorCode.PricingMissing,
|
|
78
|
+
$"tarifa de {Kind} aplicada a um pedido de {request.Kind}: a unidade de cobranca e outra.");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (Kind == MediaKind.Image)
|
|
82
|
+
{
|
|
83
|
+
var unit = (request.Options.Quality ?? MediaQuality.Low) switch
|
|
84
|
+
{
|
|
85
|
+
MediaQuality.High => _high,
|
|
86
|
+
MediaQuality.Medium => _medium,
|
|
87
|
+
_ => _low,
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
return MediaResult<decimal>.Ok(unit * Math.Max(1, request.Options.Count ?? 1));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (request.Options.DurationSeconds is not { } seconds)
|
|
94
|
+
{
|
|
95
|
+
return MediaResult<decimal>.Fail(
|
|
96
|
+
MediaErrorCode.PricingMissing,
|
|
97
|
+
"pedido de video sem durationSeconds: video cobra por segundo, e arbitrar segundos e arbitrar dinheiro.");
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return MediaResult<decimal>.Ok(_perSecond * seconds * Math.Max(1, request.Options.Count ?? 1));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
namespace Morph.AiKit.Media;
|
|
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
|
+
public sealed record MediaProvenance(
|
|
35
|
+
string Provider,
|
|
36
|
+
string ModelId,
|
|
37
|
+
string PromptFinal,
|
|
38
|
+
string? RevisedPrompt,
|
|
39
|
+
IReadOnlyList<string> RefsHash,
|
|
40
|
+
string Options,
|
|
41
|
+
string? ResponseId,
|
|
42
|
+
decimal EstimatedCostUsd,
|
|
43
|
+
DateTimeOffset CreatedAt,
|
|
44
|
+
bool C2paPreserved);
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
using System.Security.Cryptography;
|
|
2
|
+
|
|
3
|
+
namespace Morph.AiKit.Media;
|
|
4
|
+
|
|
5
|
+
/// <summary>Que tipo de midia o pedido produz. Decide provider, tarifa e limites.</summary>
|
|
6
|
+
public enum MediaKind
|
|
7
|
+
{
|
|
8
|
+
/// <summary>Imagem.</summary>
|
|
9
|
+
Image = 0,
|
|
10
|
+
|
|
11
|
+
/// <summary>Video.</summary>
|
|
12
|
+
Video = 1,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/// <summary>
|
|
16
|
+
/// Para que serve a midia pedida.
|
|
17
|
+
/// </summary>
|
|
18
|
+
/// <remarks>
|
|
19
|
+
/// <b>E dado, nao convencao de prompt.</b> A finalidade decide qualidade minima, teto de
|
|
20
|
+
/// custo, se a midia pode ser publicada e se a marcacao visivel e obrigatoria — decisoes que
|
|
21
|
+
/// o codigo precisa tomar. Deixa-las implicitas numa frase do prompt e como uma regra vira
|
|
22
|
+
/// folclore; as duas ultimas sao lidas por <see cref="MediaPurposeExtensions"/> e cobradas em
|
|
23
|
+
/// <see cref="MediaJob.Publish"/>.
|
|
24
|
+
/// </remarks>
|
|
25
|
+
public enum MediaPurpose
|
|
26
|
+
{
|
|
27
|
+
/// <summary>
|
|
28
|
+
/// Foto de catalogo. Revisao humana obrigatoria; qualidade alta na aprovada; marcacao
|
|
29
|
+
/// visivel obrigatoria para publicar — cobrada por <see cref="MediaJob.Publish"/>.
|
|
30
|
+
/// </summary>
|
|
31
|
+
CatalogPhoto = 0,
|
|
32
|
+
|
|
33
|
+
/// <summary>
|
|
34
|
+
/// Peca de marketing. Revisao humana obrigatoria; marcacao visivel obrigatoria — e a
|
|
35
|
+
/// obrigatoriedade e COBRADA por <see cref="MediaJob.Publish"/>, nao prometida aqui.
|
|
36
|
+
/// </summary>
|
|
37
|
+
Marketing = 1,
|
|
38
|
+
|
|
39
|
+
/// <summary>
|
|
40
|
+
/// Rascunho interno. <b>Nunca publicavel</b> — e <see cref="MediaJob.Publish"/> recusa,
|
|
41
|
+
/// em vez de deixar a frase valendo sozinha.
|
|
42
|
+
/// </summary>
|
|
43
|
+
Draft = 2,
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/// <summary>Qualidade pedida. <c>null</c> em <see cref="MediaOptions"/> = nao envia ao provider.</summary>
|
|
47
|
+
public enum MediaQuality
|
|
48
|
+
{
|
|
49
|
+
/// <summary>Candidatas. A escada de custo comeca aqui, sempre.</summary>
|
|
50
|
+
Low = 0,
|
|
51
|
+
|
|
52
|
+
/// <summary>Meio-termo.</summary>
|
|
53
|
+
Medium = 1,
|
|
54
|
+
|
|
55
|
+
/// <summary>So na vencedora, depois da escolha humana.</summary>
|
|
56
|
+
High = 2,
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/// <summary>
|
|
60
|
+
/// Uma imagem de referencia do pedido — a foto real do produto que a edicao preserva.
|
|
61
|
+
/// </summary>
|
|
62
|
+
/// <remarks>
|
|
63
|
+
/// <see cref="Sha256Hex"/> e calculado no construtor e entra na chave de idempotencia do
|
|
64
|
+
/// PEDIDO: trocar a foto de referencia <b>e</b> outro pedido, mesmo com o prompt identico.
|
|
65
|
+
/// Por isso o hash de CADA referencia participa, e nao a contagem delas.
|
|
66
|
+
/// </remarks>
|
|
67
|
+
public sealed class MediaReference
|
|
68
|
+
{
|
|
69
|
+
/// <summary>Cria a referencia e ja fixa o hash do conteudo.</summary>
|
|
70
|
+
/// <param name="name">Como o prompt se refere a ela ("image 1 = o sofa").</param>
|
|
71
|
+
/// <param name="mediaType">MIME dos bytes.</param>
|
|
72
|
+
/// <param name="bytes">O conteudo.</param>
|
|
73
|
+
public MediaReference(string name, string mediaType, ReadOnlyMemory<byte> bytes)
|
|
74
|
+
{
|
|
75
|
+
ArgumentException.ThrowIfNullOrWhiteSpace(name);
|
|
76
|
+
ArgumentException.ThrowIfNullOrWhiteSpace(mediaType);
|
|
77
|
+
|
|
78
|
+
Name = name;
|
|
79
|
+
MediaType = mediaType;
|
|
80
|
+
Bytes = bytes;
|
|
81
|
+
Sha256Hex = Convert.ToHexStringLower(SHA256.HashData(bytes.Span));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/// <summary>Como o prompt indexa esta referencia.</summary>
|
|
85
|
+
public string Name { get; }
|
|
86
|
+
|
|
87
|
+
/// <summary>MIME dos bytes.</summary>
|
|
88
|
+
public string MediaType { get; }
|
|
89
|
+
|
|
90
|
+
/// <summary>O conteudo.</summary>
|
|
91
|
+
public ReadOnlyMemory<byte> Bytes { get; }
|
|
92
|
+
|
|
93
|
+
/// <summary>sha256 do conteudo, em hex minusculo.</summary>
|
|
94
|
+
public string Sha256Hex { get; }
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/// <summary>
|
|
98
|
+
/// Opcoes do pedido. <b><c>null</c> significa "nao envia ao provider"</b> — nunca "manda o
|
|
99
|
+
/// default do kit": inventar default aqui e afirmar sobre o provider o que o kit nao sabe.
|
|
100
|
+
/// </summary>
|
|
101
|
+
public sealed record MediaOptions
|
|
102
|
+
{
|
|
103
|
+
/// <summary>Nenhuma opcao declarada.</summary>
|
|
104
|
+
public static readonly MediaOptions None = new();
|
|
105
|
+
|
|
106
|
+
/// <summary>Qualidade pedida.</summary>
|
|
107
|
+
public MediaQuality? Quality { get; init; }
|
|
108
|
+
|
|
109
|
+
/// <summary>Quantas candidatas numa chamada. Cobra por unidade.</summary>
|
|
110
|
+
public int? Count { get; init; }
|
|
111
|
+
|
|
112
|
+
/// <summary>Largura em pixels.</summary>
|
|
113
|
+
public int? Width { get; init; }
|
|
114
|
+
|
|
115
|
+
/// <summary>Altura em pixels.</summary>
|
|
116
|
+
public int? Height { get; init; }
|
|
117
|
+
|
|
118
|
+
/// <summary>MIME de saida.</summary>
|
|
119
|
+
public string? MediaType { get; init; }
|
|
120
|
+
|
|
121
|
+
/// <summary>Duracao, para video. O Veo aceita 4, 6 ou 8 s — e exige 8 s com referencias.</summary>
|
|
122
|
+
public int? DurationSeconds { get; init; }
|
|
123
|
+
|
|
124
|
+
/// <summary>Proporcao, para video ("16:9", "9:16").</summary>
|
|
125
|
+
public string? AspectRatio { get; init; }
|
|
126
|
+
|
|
127
|
+
/// <summary>
|
|
128
|
+
/// Forma canonica das opcoes, para a chave de idempotencia. Ordem fixa e escrita a mao
|
|
129
|
+
/// de proposito: reflexao sobre as propriedades faria a chave mudar quando alguem
|
|
130
|
+
/// reordenasse o tipo, e uma chave que muda sozinha refaz a chamada paga.
|
|
131
|
+
/// </summary>
|
|
132
|
+
public string ToCanonicalString()
|
|
133
|
+
=> string.Join('|',
|
|
134
|
+
$"quality={Quality?.ToString() ?? "-"}",
|
|
135
|
+
$"count={Count?.ToString() ?? "-"}",
|
|
136
|
+
$"w={Width?.ToString() ?? "-"}",
|
|
137
|
+
$"h={Height?.ToString() ?? "-"}",
|
|
138
|
+
$"mime={MediaType ?? "-"}",
|
|
139
|
+
$"dur={DurationSeconds?.ToString() ?? "-"}",
|
|
140
|
+
$"ar={AspectRatio ?? "-"}");
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/// <summary>
|
|
144
|
+
/// O pedido de midia: tudo o que decide a chamada paga, e nada mais.
|
|
145
|
+
/// </summary>
|
|
146
|
+
/// <param name="Alias">O apelido do <c>model-registry.v2.json</c>. O pedido nunca cita modelo.</param>
|
|
147
|
+
/// <param name="Prompt">O prompt final, ja montado pelo template versionado.</param>
|
|
148
|
+
/// <param name="Purpose">Para que serve. Ver <see cref="MediaPurpose"/>.</param>
|
|
149
|
+
public sealed record MediaRequest(string Alias, string Prompt, MediaPurpose Purpose)
|
|
150
|
+
{
|
|
151
|
+
/// <summary>Imagem ou video.</summary>
|
|
152
|
+
public MediaKind Kind { get; init; } = MediaKind.Image;
|
|
153
|
+
|
|
154
|
+
/// <summary>As referencias de produto. Vazio = geracao do zero.</summary>
|
|
155
|
+
public IReadOnlyList<MediaReference> References { get; init; } = [];
|
|
156
|
+
|
|
157
|
+
/// <summary>As opcoes. Ver <see cref="MediaOptions"/>.</summary>
|
|
158
|
+
public MediaOptions Options { get; init; } = MediaOptions.None;
|
|
159
|
+
|
|
160
|
+
/// <summary>
|
|
161
|
+
/// Recusa um pedido sem o minimo. Result, e nao excecao: pedido malformado vem de
|
|
162
|
+
/// entrada de usuario, que e caminho previsto.
|
|
163
|
+
/// </summary>
|
|
164
|
+
public MediaResult<MediaRequest> Validate()
|
|
165
|
+
{
|
|
166
|
+
if (string.IsNullOrWhiteSpace(Alias))
|
|
167
|
+
{
|
|
168
|
+
return MediaResult<MediaRequest>.Fail(
|
|
169
|
+
MediaErrorCode.InvalidRequest, "alias vazio: o pedido nao sabe a que modelo ir.");
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (string.IsNullOrWhiteSpace(Prompt))
|
|
173
|
+
{
|
|
174
|
+
return MediaResult<MediaRequest>.Fail(
|
|
175
|
+
MediaErrorCode.InvalidRequest, "prompt vazio: nao ha o que gerar.");
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (Options.Count is { } count && count <= 0)
|
|
179
|
+
{
|
|
180
|
+
return MediaResult<MediaRequest>.Fail(
|
|
181
|
+
MediaErrorCode.InvalidRequest, $"count={count}: pedir zero candidatas nao e um pedido.");
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (Kind == MediaKind.Video && Options.DurationSeconds is { } seconds && seconds <= 0)
|
|
185
|
+
{
|
|
186
|
+
return MediaResult<MediaRequest>.Fail(
|
|
187
|
+
MediaErrorCode.InvalidRequest, $"durationSeconds={seconds}: video de duracao nao positiva.");
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return MediaResult<MediaRequest>.Ok(this);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
namespace Morph.AiKit.Media;
|
|
2
|
+
|
|
3
|
+
/// <summary>
|
|
4
|
+
/// Por que uma operacao de midia recusou. Discriminado por codigo, e nao por tipo de
|
|
5
|
+
/// excecao, porque quem trata precisa do MOTIVO — e porque recusa aqui e caminho previsto,
|
|
6
|
+
/// nunca fluxo de controle por excecao.
|
|
7
|
+
/// </summary>
|
|
8
|
+
public enum MediaErrorCode
|
|
9
|
+
{
|
|
10
|
+
/// <summary>O pedido nao tem o minimo para ser aceito (prompt vazio, alias vazio).</summary>
|
|
11
|
+
InvalidRequest = 0,
|
|
12
|
+
|
|
13
|
+
/// <summary>O teto de custo do tenant/dia recusaria a chamada. Recusa no ENQUEUE.</summary>
|
|
14
|
+
BudgetExceeded = 1,
|
|
15
|
+
|
|
16
|
+
/// <summary>Falha tecnica do provider (429, 5xx, timeout). Retryable.</summary>
|
|
17
|
+
ProviderFailure = 2,
|
|
18
|
+
|
|
19
|
+
/// <summary>
|
|
20
|
+
/// Bloqueio de moderacao do provider. <b>Terminal</b>: retentar gasta para receber o
|
|
21
|
+
/// mesmo "nao", e em provider que cobra o bloqueio gasta duas vezes.
|
|
22
|
+
/// </summary>
|
|
23
|
+
PolicyBlocked = 3,
|
|
24
|
+
|
|
25
|
+
/// <summary>A transicao pedida nao existe a partir do estado atual do job.</summary>
|
|
26
|
+
InvalidTransition = 4,
|
|
27
|
+
|
|
28
|
+
/// <summary>O job pedido nao existe no acervo do projeto.</summary>
|
|
29
|
+
NotFound = 5,
|
|
30
|
+
|
|
31
|
+
/// <summary>Nao ha tarifa declarada para o alias — o custo nao pode ser estimado.</summary>
|
|
32
|
+
PricingMissing = 6,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/// <summary>Uma recusa, com codigo e frase.</summary>
|
|
36
|
+
/// <param name="Code">O motivo, para quem decide.</param>
|
|
37
|
+
/// <param name="Message">A frase, para quem le o log.</param>
|
|
38
|
+
public sealed record MediaError(MediaErrorCode Code, string Message);
|
|
39
|
+
|
|
40
|
+
/// <summary>
|
|
41
|
+
/// Result do subsistema de midia.
|
|
42
|
+
/// </summary>
|
|
43
|
+
/// <remarks>
|
|
44
|
+
/// <para>
|
|
45
|
+
/// <b>Por que <c>MediaResult</c> e nao <c>Result</c>:</b> este kit nao tem — e nao ganha
|
|
46
|
+
/// aqui — um Result de proposito geral. Introduzir um tipo generico na raiz de
|
|
47
|
+
/// <c>Morph.AiKit</c> e uma decisao transversal, que afeta toda superficie futura do kit e
|
|
48
|
+
/// que uma feature de midia nao deve tomar de passagem. O escopo esta no nome.
|
|
49
|
+
/// </para>
|
|
50
|
+
/// <para>
|
|
51
|
+
/// Excecao continua existindo para <b>falha de composicao</b> (argumento nulo na
|
|
52
|
+
/// montagem), como na <c>AgentFactory</c>. O que nao existe e excecao para dizer "este job
|
|
53
|
+
/// nao pode ser publicado neste estado": isso e informacao, e informacao viaja em Result.
|
|
54
|
+
/// </para>
|
|
55
|
+
/// </remarks>
|
|
56
|
+
/// <typeparam name="T">O valor devolvido no sucesso.</typeparam>
|
|
57
|
+
public sealed record MediaResult<T>
|
|
58
|
+
{
|
|
59
|
+
private readonly T _value;
|
|
60
|
+
|
|
61
|
+
private MediaResult(bool isSuccess, T value, MediaError? error)
|
|
62
|
+
{
|
|
63
|
+
IsSuccess = isSuccess;
|
|
64
|
+
_value = value;
|
|
65
|
+
Error = error;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/// <summary>Se a operacao produziu valor.</summary>
|
|
69
|
+
public bool IsSuccess { get; }
|
|
70
|
+
|
|
71
|
+
/// <summary>A recusa, ou nulo quando <see cref="IsSuccess"/>.</summary>
|
|
72
|
+
public MediaError? Error { get; }
|
|
73
|
+
|
|
74
|
+
/// <summary>O valor. Ler no caminho de falha e erro de programacao, e grita.</summary>
|
|
75
|
+
/// <exception cref="InvalidOperationException">Quando a operacao falhou.</exception>
|
|
76
|
+
public T Value => IsSuccess
|
|
77
|
+
? _value
|
|
78
|
+
: throw new InvalidOperationException(
|
|
79
|
+
$"MediaResult<{typeof(T).Name}> em falha ({Error?.Code}): {Error?.Message}. " +
|
|
80
|
+
"Confira IsSuccess antes de ler Value.");
|
|
81
|
+
|
|
82
|
+
/// <summary>Sucesso com valor.</summary>
|
|
83
|
+
public static MediaResult<T> Ok(T value) => new(true, value, null);
|
|
84
|
+
|
|
85
|
+
/// <summary>Recusa nomeada.</summary>
|
|
86
|
+
public static MediaResult<T> Fail(MediaErrorCode code, string message)
|
|
87
|
+
=> new(false, default!, new MediaError(code, message));
|
|
88
|
+
|
|
89
|
+
/// <summary>Repassa a recusa de outro Result, preservando codigo e frase.</summary>
|
|
90
|
+
public static MediaResult<T> FailLike<TOther>(MediaResult<TOther> other)
|
|
91
|
+
=> other.Error is { } e
|
|
92
|
+
? new MediaResult<T>(false, default!, e)
|
|
93
|
+
: throw new InvalidOperationException("FailLike sobre um Result de sucesso.");
|
|
94
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
namespace Morph.AiKit.Media;
|
|
2
|
+
|
|
3
|
+
/// <summary>O veredito de um humano sobre uma midia gerada.</summary>
|
|
4
|
+
public enum MediaReviewDecision
|
|
5
|
+
{
|
|
6
|
+
/// <summary>Aprovada. Unica decisao que leva a <see cref="MediaJobState.Approved"/>.</summary>
|
|
7
|
+
Approved = 0,
|
|
8
|
+
|
|
9
|
+
/// <summary>Recusada.</summary>
|
|
10
|
+
Rejected = 1,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/// <summary>
|
|
14
|
+
/// Quem decidiu, o que decidiu, quando, e por que.
|
|
15
|
+
/// </summary>
|
|
16
|
+
/// <remarks>
|
|
17
|
+
/// <para>
|
|
18
|
+
/// <b>"Aprovado" sem quem e sem quando nao e revisao, e um campo booleano.</b> Por isso
|
|
19
|
+
/// <see cref="Reviewer"/> e <see cref="DecidedAt"/> sao obrigatorios na construcao, e a
|
|
20
|
+
/// construcao recusa um revisor em branco: um carimbo anonimo nao responde a pergunta que
|
|
21
|
+
/// alguem vai fazer seis meses depois.
|
|
22
|
+
/// </para>
|
|
23
|
+
/// <para>
|
|
24
|
+
/// <b>Este tipo nao reimplementa as transicoes.</b> Elas moram no <see cref="MediaJob"/>,
|
|
25
|
+
/// que e o dono do estado; os metodos estaticos daqui sao a forma conveniente de aplicar
|
|
26
|
+
/// uma revisao, e delegam. Uma segunda copia da regra "so Approved publica" e exatamente
|
|
27
|
+
/// como as duas divergem.
|
|
28
|
+
/// </para>
|
|
29
|
+
/// <para>
|
|
30
|
+
/// Um juiz LLM pode preencher <see cref="Note"/> como pre-filtro, mas nao pode ser o
|
|
31
|
+
/// <see cref="Reviewer"/> de um <c>CatalogPhoto</c>: ele economiza o tempo do humano, nao
|
|
32
|
+
/// assume a responsabilidade por um claim visual.
|
|
33
|
+
/// </para>
|
|
34
|
+
/// </remarks>
|
|
35
|
+
public sealed record MediaReview
|
|
36
|
+
{
|
|
37
|
+
private MediaReview(string reviewer, MediaReviewDecision decision, DateTimeOffset decidedAt, string? note)
|
|
38
|
+
{
|
|
39
|
+
Reviewer = reviewer;
|
|
40
|
+
Decision = decision;
|
|
41
|
+
DecidedAt = decidedAt;
|
|
42
|
+
Note = note;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/// <summary>Identificador do humano que decidiu.</summary>
|
|
46
|
+
public string Reviewer { get; }
|
|
47
|
+
|
|
48
|
+
/// <summary>O veredito.</summary>
|
|
49
|
+
public MediaReviewDecision Decision { get; }
|
|
50
|
+
|
|
51
|
+
/// <summary>Quando.</summary>
|
|
52
|
+
public DateTimeOffset DecidedAt { get; }
|
|
53
|
+
|
|
54
|
+
/// <summary>Observacao livre — inclusive o veredito de um juiz LLM usado como pre-filtro.</summary>
|
|
55
|
+
public string? Note { get; }
|
|
56
|
+
|
|
57
|
+
/// <summary>Uma aprovacao humana.</summary>
|
|
58
|
+
/// <exception cref="ArgumentException">Revisor em branco.</exception>
|
|
59
|
+
public static MediaReview ApprovedBy(string reviewer, DateTimeOffset decidedAt, string? note = null)
|
|
60
|
+
{
|
|
61
|
+
ArgumentException.ThrowIfNullOrWhiteSpace(reviewer);
|
|
62
|
+
return new MediaReview(reviewer, MediaReviewDecision.Approved, decidedAt, note);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/// <summary>Uma recusa humana.</summary>
|
|
66
|
+
/// <exception cref="ArgumentException">Revisor em branco.</exception>
|
|
67
|
+
public static MediaReview RejectedBy(string reviewer, DateTimeOffset decidedAt, string? note = null)
|
|
68
|
+
{
|
|
69
|
+
ArgumentException.ThrowIfNullOrWhiteSpace(reviewer);
|
|
70
|
+
return new MediaReview(reviewer, MediaReviewDecision.Rejected, decidedAt, note);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/// <summary>Aprova o job. Delega a <see cref="MediaJob.Approve"/> — nao reimplementa a regra.</summary>
|
|
74
|
+
public static MediaResult<MediaJob> Approve(MediaJob job, string reviewer, DateTimeOffset decidedAt, string? note = null)
|
|
75
|
+
{
|
|
76
|
+
ArgumentNullException.ThrowIfNull(job);
|
|
77
|
+
return job.Approve(ApprovedBy(reviewer, decidedAt, note));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/// <summary>Recusa o job. Delega a <see cref="MediaJob.Reject"/>.</summary>
|
|
81
|
+
public static MediaResult<MediaJob> Reject(MediaJob job, string reviewer, DateTimeOffset decidedAt, string? note = null)
|
|
82
|
+
{
|
|
83
|
+
ArgumentNullException.ThrowIfNull(job);
|
|
84
|
+
return job.Reject(RejectedBy(reviewer, decidedAt, note));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/// <summary>
|
|
88
|
+
/// Publica o job. Delega a <see cref="MediaJob.Publish"/>, que cobra as tres portas
|
|
89
|
+
/// (estado Approved, finalidade publicavel, marcacao quando exigida).
|
|
90
|
+
/// </summary>
|
|
91
|
+
public static MediaResult<MediaJob> Publish(
|
|
92
|
+
MediaJob job,
|
|
93
|
+
MediaDisclosure? disclosure,
|
|
94
|
+
DateTimeOffset publishedAt)
|
|
95
|
+
{
|
|
96
|
+
ArgumentNullException.ThrowIfNull(job);
|
|
97
|
+
return job.Publish(disclosure, publishedAt);
|
|
98
|
+
}
|
|
99
|
+
}
|