@polymorphism-tech/morph-spec 8.34.2 → 8.35.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +22 -5
- package/README.md +2 -2
- package/bin/morph-spec.js +18 -4
- package/bin/validate.js +379 -379
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +5 -4
- package/docs/QUICKSTART.md +1 -1
- package/framework/MORPH.md +16 -17
- package/framework/agents.json +197 -44
- package/framework/ai-pin.json +22 -0
- package/framework/commands/morph-media.md +45 -0
- package/framework/docs/CLI.md +236 -11
- 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/claude-code/core/protect-spec-files.js +41 -4
- package/framework/hooks/claude-code/core/worktree-env-inject.js +43 -0
- package/framework/hooks/claude-code/statusline.sh +11 -11
- package/framework/hooks/git/commit-msg/conventional-commits.sh +33 -33
- package/framework/hooks/git/pre-commit/agents.sh +25 -25
- package/framework/hooks/git/pre-commit/orchestrator.sh +64 -64
- package/framework/hooks/git/pre-commit/specs.sh +50 -50
- package/framework/hooks/git/pre-push/run-tests.sh +44 -44
- package/framework/hooks/shared/e2e-check.js +135 -22
- package/framework/hooks/shared/payload-utils.js +15 -2
- package/framework/hooks/shared/stdin-reader.js +26 -26
- package/framework/rules/ai-agents-standards.md +14 -2
- 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 +26 -5
- 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 +18 -2
- package/framework/skills/core/morph-media/SKILL.md +215 -0
- package/framework/skills/core/morph-plan/SKILL.md +33 -2
- 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 +1483 -839
- package/framework/standards/ai-agents/agent-archetypes.md +160 -0
- package/framework/standards/ai-agents/agent-session.md +164 -68
- package/framework/standards/ai-agents/agent-spec.md +196 -0
- package/framework/standards/ai-agents/batching.md +61 -37
- package/framework/standards/ai-agents/context-providers.md +97 -24
- 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 +119 -18
- package/framework/standards/ai-agents/mcp-tools.md +85 -32
- 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 +39 -5
- package/framework/standards/ai-agents/modalities-image-gen.md +354 -105
- package/framework/standards/ai-agents/modalities-speech-to-text.md +8 -2
- package/framework/standards/ai-agents/modalities-text-to-speech.md +10 -2
- package/framework/standards/ai-agents/modalities-vision.md +6 -2
- package/framework/standards/ai-agents/multi-agent-patterns.md +61 -5
- package/framework/standards/ai-agents/observability-patterns.md +227 -17
- package/framework/standards/ai-agents/production.md +38 -9
- package/framework/standards/ai-agents/prompt-sources.md +389 -0
- package/framework/standards/ai-agents/providers/model-registry.md +174 -39
- package/framework/standards/ai-agents/rag-custom-pgvector.md +69 -4
- package/framework/standards/ai-agents/service-tier-flex.md +27 -19
- package/framework/standards/ai-agents/setup.md +166 -50
- package/framework/standards/ai-agents/structured-output.md +34 -3
- package/framework/standards/ai-agents/sweet-spot.md +23 -3
- 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 +3 -1
- package/framework/templates/.idea/morph-templates.xml +92 -0
- package/framework/templates/.vscode/morph-templates.code-snippets +186 -0
- package/framework/templates/IDE-SNIPPETS.md +262 -262
- package/framework/templates/REGISTRY.json +13 -0
- package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
- package/framework/templates/code/dotnet/ai-agents/AgentBootstrap.cs.template +39 -39
- package/framework/templates/code/dotnet/ai-agents/EmbeddingService.cs.template +68 -68
- package/framework/templates/code/dotnet/ai-agents/ModelRegistry.cs.template +117 -117
- package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +1 -1
- package/framework/templates/code/dotnet/ai-agents/ResilientToolMiddleware.cs.template +61 -61
- package/framework/templates/code/dotnet/ai-agents/VectorSearchTool.cs.template +107 -107
- package/framework/templates/code/dotnet/ai-agents/model-registry.json.template +58 -58
- package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +281 -281
- package/framework/templates/code/dotnet/database/migration.cs +83 -83
- package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +21 -21
- package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -4
- package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -4
- package/framework/templates/code/dotnet/feature/validator.cs.hbs +15 -15
- package/framework/templates/code/sql/ai-agents/agent-prompts-migration.sql.template +1 -1
- package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -91
- package/framework/templates/code/typescript/contracts.ts +167 -167
- package/framework/templates/docs/clarifications.md +253 -253
- package/framework/templates/docs/schema-analysis.md +119 -119
- package/framework/templates/docs/ui-components.md +52 -52
- package/framework/templates/docs/ui-design-system.md +76 -76
- package/framework/templates/docs/ui-flows.md +167 -167
- package/framework/templates/docs/ui-mockups.md +98 -98
- 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/feature/decisions.md +122 -122
- package/framework/templates/frontend/nextjs/Dockerfile.nextjs.hbs +43 -43
- package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -26
- package/framework/templates/frontend/nextjs/env.mjs.hbs +32 -32
- package/framework/templates/frontend/nextjs/feature-form.tsx.hbs +56 -56
- package/framework/templates/frontend/nextjs/page.tsx.hbs +22 -22
- package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -26
- package/framework/templates/frontend/nextjs/use-feature.ts.hbs +54 -54
- package/framework/templates/infrastructure/docker/Dockerfile.template +58 -58
- package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
- package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
- package/framework/templates/infrastructure/github/README.md +56 -56
- package/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +45 -45
- package/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +27 -27
- package/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +59 -59
- package/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +39 -39
- package/framework/templates/integrations/asaas-client.cs +387 -387
- package/framework/templates/integrations/asaas-webhook.cs +351 -351
- package/framework/templates/meta-prompts/fusion/fusion-agent.md +76 -76
- package/framework/templates/meta-prompts/fusion/fusion-aggregator.md +100 -100
- package/framework/templates/meta-prompts/validators/pre-commit-validator.md +95 -95
- package/framework/templates/project/validate.js +120 -120
- package/framework/templates/state.template.json +17 -17
- package/framework/templates/ui/design-system.css +226 -226
- package/package.json +3 -3
- package/src/commands/dashboard/dashboard.js +53 -53
- package/src/commands/e2e/e2e.js +15 -2
- package/src/commands/index.js +13 -13
- package/src/commands/project/checkpoint.js +209 -209
- package/src/commands/project/doctor.js +148 -6
- package/src/commands/project/update.js +469 -153
- package/src/commands/project/worktree.js +76 -4
- 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/finish.js +379 -24
- 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/phase-state-machine.js +197 -197
- package/src/core/state/state-manager.js +9 -1
- package/src/core/templates/template-renderer.js +477 -477
- package/src/dashboard/app/components/DetailPanel.jsx +11 -11
- package/src/dashboard/app/components/FlowGraph.jsx +17 -17
- package/src/dashboard/app/components/SearchFilter.jsx +22 -22
- package/src/dashboard/app/hooks/useDetailPanel.js +15 -15
- package/src/dashboard/app/main.jsx +13 -13
- package/src/dashboard/app/pages/AgentMap.jsx +370 -370
- package/src/dashboard/app/pages/PhasePipeline.jsx +408 -408
- package/src/dashboard/app/pages/RulesMap.jsx +273 -273
- package/src/dashboard/app/pages/SkillsLibrary.jsx +366 -366
- package/src/dashboard/app/pages/Standards.jsx +332 -332
- package/src/dashboard/app/styles/index.css +47 -47
- package/src/dashboard/index.html +12 -12
- package/src/dashboard/package.json +24 -24
- package/src/dashboard/scanner/phases-scanner.js +15 -15
- package/src/dashboard/scanner/rubrics-scanner.js +12 -12
- package/src/dashboard/scanner/rules-scanner.js +37 -37
- package/src/dashboard/scanner/skills-scanner.js +43 -43
- package/src/dashboard/scanner/squads-scanner.js +13 -13
- package/src/dashboard/vite-plugin-morph-data.js +17 -17
- package/src/dashboard/vite.config.js +24 -24
- 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/detectors/design-system-detector.js +188 -188
- package/src/lib/detectors/index.js +217 -217
- package/src/lib/e2e/compose-env.js +50 -4
- package/src/lib/evaluator-report.js +269 -15
- package/src/lib/generators/design-system-generator.js +508 -508
- package/src/lib/generators/settings-generator.js +209 -209
- package/src/lib/git/default-branch.js +92 -4
- package/src/lib/git/pr-files.js +56 -0
- package/src/lib/hooks/hook-executor.js +167 -167
- package/src/lib/installers/effects.js +193 -0
- package/src/lib/installers/mcp-installer.js +719 -719
- 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/task-classifier.js +30 -30
- package/src/lib/tasks/task-parser.js +209 -209
- package/src/lib/tasks/test-nodes.js +486 -0
- package/src/lib/tasks/test-runner.js +410 -33
- package/src/lib/validators/checkpoint-persistence.js +39 -39
- package/src/lib/validators/css/css-validator.js +340 -340
- package/src/lib/validators/shared/emit-validator-dispatch.js +14 -14
- package/src/lib/validators/shared/issue-counter.js +14 -14
- package/src/lib/validators/verify-runner.js +583 -28
- package/src/lib/worktree/docker-audit.js +287 -0
- package/src/lib/worktree/env-allocator.js +24 -0
- package/src/lib/worktree/worktree-audit.js +30 -1
- package/src/scripts/setup-infra.js +24 -11
- package/src/utils/agent-id-mapper.js +11 -11
- package/src/utils/agents-installer.js +18 -8
- package/src/utils/claude-md-injector.js +10 -5
- package/src/utils/claude-settings-manager.js +300 -300
- package/src/utils/file-copier.js +11 -4
- package/src/utils/hooks-installer.js +29 -26
- package/src/utils/skills-installer.js +36 -29
- package/src/utils/version-checker.js +68 -3
- package/framework/standards/ai-agents/prompts-in-db.md +0 -214
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
using Microsoft.Extensions.AI;
|
|
2
|
+
using Microsoft.Extensions.AI.Evaluation;
|
|
3
|
+
using Microsoft.Extensions.AI.Evaluation.Reporting;
|
|
4
|
+
using Microsoft.Extensions.AI.Evaluation.Reporting.Storage;
|
|
5
|
+
|
|
6
|
+
namespace Morph.AiKit.Evals;
|
|
7
|
+
|
|
8
|
+
/// <summary>O que uma execucao de cenario produziu.</summary>
|
|
9
|
+
/// <param name="Scenario">A chave do cenario na cache (<c>{agente}/{tenant}/{nome}</c>).</param>
|
|
10
|
+
/// <param name="Passed">Se TODAS as assercoes deterministicas se sustentaram.</param>
|
|
11
|
+
/// <param name="Outcomes">Um veredito por assercao, na ordem do dataset.</param>
|
|
12
|
+
/// <param name="Result">O resultado completo da biblioteca, com as metricas de juizo junto.</param>
|
|
13
|
+
public sealed record EvalScenarioResult(
|
|
14
|
+
string Scenario,
|
|
15
|
+
bool Passed,
|
|
16
|
+
IReadOnlyList<EvalAssertionOutcome> Outcomes,
|
|
17
|
+
EvaluationResult Result);
|
|
18
|
+
|
|
19
|
+
/// <summary>
|
|
20
|
+
/// Monta a configuracao de eval com cache em disco de TTL explicito, roda os cenarios do
|
|
21
|
+
/// dataset versionado e escreve o sumario que o CLI le.
|
|
22
|
+
/// </summary>
|
|
23
|
+
/// <remarks>
|
|
24
|
+
/// <para>
|
|
25
|
+
/// <b>A montagem inteira existe para uma frase: com a cache quente, o eval roda sem
|
|
26
|
+
/// credencial e sem rede.</b> O caminho e este — a biblioteca embrulha o cliente interno num
|
|
27
|
+
/// cliente de cache; no HIT o interno nunca e invocado; em modo offline o interno e o
|
|
28
|
+
/// <see cref="OfflineChatClient"/>, que recusa. Logo, ou tudo resolve pela cache, ou a suite
|
|
29
|
+
/// fica vermelha nomeando o cenario que faltou. Nunca <c>skip</c>: gate verde sobre
|
|
30
|
+
/// verificacao que nao aconteceu e o modo de falha que este harness mais paga para evitar.
|
|
31
|
+
/// </para>
|
|
32
|
+
/// <para>
|
|
33
|
+
/// <b>Cache e resultados vivem em raizes DIFERENTES</b>, e isso e uma decisao, nao um
|
|
34
|
+
/// detalhe: os resultados carregam latencia, uso de tokens e o flag de cache hit, todos
|
|
35
|
+
/// nao-deterministicos. Numa raiz so, commitar a cache arrastaria os resultados junto e
|
|
36
|
+
/// produziria diff em toda rodada, sem sinal nenhum. Separar por CONSTRUCAO (duas raizes) e
|
|
37
|
+
/// mais forte que separar por <c>.gitignore</c>: uma regra de ignore dentro de um diretorio
|
|
38
|
+
/// commitado e uma regra que a proxima pessoa remove sem entender.
|
|
39
|
+
/// </para>
|
|
40
|
+
/// <para>
|
|
41
|
+
/// <b>Uma <c>ReportingConfiguration</c> por cenario</b>, e nao uma para a suite, porque as
|
|
42
|
+
/// assercoes deterministicas SAO os avaliadores e elas mudam a cada cenario. A alternativa
|
|
43
|
+
/// seria um avaliador com estado mutavel entre cenarios — barato de escrever e caro de
|
|
44
|
+
/// depurar no dia em que dois cenarios rodarem fora de ordem.
|
|
45
|
+
/// </para>
|
|
46
|
+
/// </remarks>
|
|
47
|
+
public sealed class EvalFixture : IAsyncDisposable
|
|
48
|
+
{
|
|
49
|
+
/// <summary>
|
|
50
|
+
/// O nome do cenario em execucao, por indirecao.
|
|
51
|
+
/// </summary>
|
|
52
|
+
/// <remarks>
|
|
53
|
+
/// O <see cref="OfflineChatClient"/> e construido ANTES do fixture (ele e o cliente
|
|
54
|
+
/// interno dele) e precisa nomear, na mensagem de miss, o cenario que esta rodando
|
|
55
|
+
/// AGORA. Um nome capturado na construcao nomearia sempre o primeiro cenario, e a
|
|
56
|
+
/// mensagem mandaria o leitor investigar o cenario errado.
|
|
57
|
+
/// </remarks>
|
|
58
|
+
private sealed class ScenarioCursor
|
|
59
|
+
{
|
|
60
|
+
public string Name { get; set; } = "(nenhum)";
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private readonly List<EvalScenarioResult> _results = [];
|
|
64
|
+
private readonly ScenarioCursor _cursor;
|
|
65
|
+
private readonly IChatClient _inner;
|
|
66
|
+
private readonly OfflineChatClient? _offline;
|
|
67
|
+
private readonly CountingChatClient? _counting;
|
|
68
|
+
private readonly IReadOnlyList<IEvaluator> _quality;
|
|
69
|
+
private readonly DiskBasedResultStore _resultStore;
|
|
70
|
+
|
|
71
|
+
private EvalFixture(
|
|
72
|
+
EvalCacheOptions options,
|
|
73
|
+
EvalCacheStampCheck stampCheck,
|
|
74
|
+
ScenarioCursor cursor,
|
|
75
|
+
IChatClient inner,
|
|
76
|
+
OfflineChatClient? offline,
|
|
77
|
+
CountingChatClient? counting,
|
|
78
|
+
IReadOnlyList<IEvaluator> quality)
|
|
79
|
+
{
|
|
80
|
+
Options = options;
|
|
81
|
+
StampCheck = stampCheck;
|
|
82
|
+
_cursor = cursor;
|
|
83
|
+
_inner = inner;
|
|
84
|
+
_offline = offline;
|
|
85
|
+
_counting = counting;
|
|
86
|
+
_quality = quality;
|
|
87
|
+
_resultStore = new DiskBasedResultStore(options.ResultsRoot);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/// <summary>Nome da execucao no store de resultados. Estavel, porque os resultados nao se commitam.</summary>
|
|
91
|
+
public const string ExecutionName = "morph-eval";
|
|
92
|
+
|
|
93
|
+
/// <summary>A configuracao efetiva desta execucao.</summary>
|
|
94
|
+
public EvalCacheOptions Options { get; }
|
|
95
|
+
|
|
96
|
+
/// <summary>
|
|
97
|
+
/// O veredito do carimbo, apurado UMA vez na construcao — antes de avaliar qualquer
|
|
98
|
+
/// cenario. Nao faz sentido pagar o relogio de uma suite para descobrir no fim que a
|
|
99
|
+
/// cache nao servia.
|
|
100
|
+
/// </summary>
|
|
101
|
+
public EvalCacheStampCheck StampCheck { get; }
|
|
102
|
+
|
|
103
|
+
/// <summary>
|
|
104
|
+
/// O cliente offline, quando o modo e <see cref="EvalMode.Offline"/>. E por ele que se
|
|
105
|
+
/// mede "zero invocacoes do cliente interno" — a prova de que nenhuma chamada paga foi
|
|
106
|
+
/// feita.
|
|
107
|
+
/// </summary>
|
|
108
|
+
public OfflineChatClient? Offline => _offline;
|
|
109
|
+
|
|
110
|
+
/// <summary>Quantas vezes a cache falhou e a chamada desceu ao cliente interno.</summary>
|
|
111
|
+
public int Misses => _offline?.Invocations ?? _counting?.Calls ?? 0;
|
|
112
|
+
|
|
113
|
+
/// <summary>Os resultados acumulados, na ordem em que os cenarios rodaram.</summary>
|
|
114
|
+
public IReadOnlyList<EvalScenarioResult> Results => _results;
|
|
115
|
+
|
|
116
|
+
/// <summary>O sumario do que rodou ate agora.</summary>
|
|
117
|
+
public EvalRunSummary Summary => new()
|
|
118
|
+
{
|
|
119
|
+
Scenarios = _results.Count,
|
|
120
|
+
Passed = _results.Count(r => r.Passed),
|
|
121
|
+
Failed = _results.Count(r => !r.Passed),
|
|
122
|
+
Cache = Misses == 0 ? EvalCacheState.Warm : EvalCacheState.Cold,
|
|
123
|
+
Stamp = StampCheck.Verdict.ToString().ToLowerInvariant(),
|
|
124
|
+
Misses = Misses,
|
|
125
|
+
Provider = Options.ProviderName,
|
|
126
|
+
Model = Options.DefaultModelId,
|
|
127
|
+
Quality = CollectQuality(),
|
|
128
|
+
Failures = CollectFailures(),
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
/// <summary>
|
|
132
|
+
/// Uma entrada por assercao REPROVADA, com o cenario que a carregava.
|
|
133
|
+
/// </summary>
|
|
134
|
+
/// <remarks>
|
|
135
|
+
/// Percorre as assercoes reprovadas, e nao os cenarios reprovados: o cenario diz ONDE, a
|
|
136
|
+
/// assercao diz O QUE. Um sumario que so contasse cenarios obrigaria quem le a reabrir o
|
|
137
|
+
/// dataset para descobrir qual regua quebrou — que e exatamente a investigacao que a
|
|
138
|
+
/// mensagem existe para dispensar.
|
|
139
|
+
/// </remarks>
|
|
140
|
+
private IReadOnlyList<EvalFailureDetail> CollectFailures()
|
|
141
|
+
{
|
|
142
|
+
var failures = new List<EvalFailureDetail>();
|
|
143
|
+
|
|
144
|
+
foreach (var result in _results.Where(r => !r.Passed))
|
|
145
|
+
{
|
|
146
|
+
var reproved = result.Outcomes.Where(o => !o.Passed).ToList();
|
|
147
|
+
|
|
148
|
+
// O cenario SEM assercao nenhuma reprova por vacuidade (ele nao cobra nada), e
|
|
149
|
+
// nesse caso nao existe outcome reprovado para nomear. Sem esta linha ele seria
|
|
150
|
+
// contado em `Failed` e invisivel em `Failures` — um numero sem frase, que e a
|
|
151
|
+
// metade exata do defeito que este tipo existe para fechar.
|
|
152
|
+
if (reproved.Count == 0)
|
|
153
|
+
{
|
|
154
|
+
failures.Add(new EvalFailureDetail(
|
|
155
|
+
result.Scenario,
|
|
156
|
+
"(nenhuma)",
|
|
157
|
+
"(nenhuma)",
|
|
158
|
+
"FALHOU: cenario sem assercao deterministica — ele nao cobra nada"));
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
failures.AddRange(reproved.Select(o => new EvalFailureDetail(
|
|
163
|
+
result.Scenario,
|
|
164
|
+
o.Assertion ?? "(assercao sem nome)",
|
|
165
|
+
o.Kind?.ToString() ?? "(regua desconhecida)",
|
|
166
|
+
o.Reason)));
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return failures;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/// <summary>
|
|
173
|
+
/// Constroi o fixture a partir das variaveis que o CLI DITA.
|
|
174
|
+
/// </summary>
|
|
175
|
+
/// <remarks>
|
|
176
|
+
/// Fora do CLI um projeto pode montar <see cref="EvalCacheOptions"/> a mao e chamar
|
|
177
|
+
/// <see cref="Create"/>; as variaveis existem para que a MESMA suite seja executavel pelo
|
|
178
|
+
/// <c>verify</c> sem um segundo caminho de configuracao.
|
|
179
|
+
/// </remarks>
|
|
180
|
+
/// <param name="liveClient">
|
|
181
|
+
/// O cliente real. Obrigatorio em <see cref="EvalMode.Refresh"/> e
|
|
182
|
+
/// <see cref="EvalMode.Online"/>; ignorado em <see cref="EvalMode.Offline"/>.
|
|
183
|
+
/// </param>
|
|
184
|
+
/// <param name="qualityEvaluators">Avaliadores de juizo. Reportados, nunca decisivos.</param>
|
|
185
|
+
/// <param name="timeProvider">Relogio, para a checagem de pin no futuro.</param>
|
|
186
|
+
public static EvalFixture FromEnvironment(
|
|
187
|
+
IChatClient? liveClient = null,
|
|
188
|
+
IEnumerable<IEvaluator>? qualityEvaluators = null,
|
|
189
|
+
TimeProvider? timeProvider = null)
|
|
190
|
+
{
|
|
191
|
+
var cacheRoot = Required("MORPH_EVAL_CACHE");
|
|
192
|
+
var beside = Path.GetDirectoryName(Path.GetFullPath(cacheRoot))
|
|
193
|
+
?? throw new InvalidOperationException($"eval-cache-path-invalid: \"{cacheRoot}\" nao tem diretorio pai.");
|
|
194
|
+
|
|
195
|
+
var options = new EvalCacheOptions
|
|
196
|
+
{
|
|
197
|
+
CacheRoot = cacheRoot,
|
|
198
|
+
DatasetRoot = Environment.GetEnvironmentVariable("MORPH_EVAL_DATASET") ?? Path.Combine(beside, "eval-datasets"),
|
|
199
|
+
ResultsRoot = Environment.GetEnvironmentVariable("MORPH_EVAL_RESULTS") ?? Path.Combine(beside, "eval-results"),
|
|
200
|
+
Mode = ParseMode(Environment.GetEnvironmentVariable("MORPH_EVAL_MODE")),
|
|
201
|
+
// Explicito e longo: o default da biblioteca (14 dias) apagaria a arvore
|
|
202
|
+
// commitada durante uma leitura. Ver EvalCacheOptions.
|
|
203
|
+
TimeToLive = TimeSpan.FromDays(3650),
|
|
204
|
+
ProviderName = Environment.GetEnvironmentVariable("MORPH_EVAL_PROVIDER") ?? "morph-eval",
|
|
205
|
+
DefaultModelId = Environment.GetEnvironmentVariable("MORPH_EVAL_MODEL"),
|
|
206
|
+
SummaryPath = Environment.GetEnvironmentVariable("MORPH_EVAL_SUMMARY"),
|
|
207
|
+
EnvFile = Environment.GetEnvironmentVariable("MORPH_EVAL_ENVFILE") ?? ".env.evals",
|
|
208
|
+
Pin = ParsePin(Environment.GetEnvironmentVariable("MORPH_EVAL_PIN")),
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
return Create(options, liveClient, qualityEvaluators, timeProvider);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/// <summary>Constroi o fixture a partir de opcoes explicitas.</summary>
|
|
215
|
+
/// <exception cref="ArgumentOutOfRangeException">TTL abaixo do piso — ver <see cref="EvalCacheOptions.MinimumTimeToLive"/>.</exception>
|
|
216
|
+
/// <exception cref="InvalidOperationException">Carimbo ausente ou divergente em modo de leitura; ou modo de escrita sem cliente real.</exception>
|
|
217
|
+
public static EvalFixture Create(
|
|
218
|
+
EvalCacheOptions options,
|
|
219
|
+
IChatClient? liveClient = null,
|
|
220
|
+
IEnumerable<IEvaluator>? qualityEvaluators = null,
|
|
221
|
+
TimeProvider? timeProvider = null)
|
|
222
|
+
{
|
|
223
|
+
ArgumentNullException.ThrowIfNull(options);
|
|
224
|
+
|
|
225
|
+
if (options.TimeToLive < EvalCacheOptions.MinimumTimeToLive)
|
|
226
|
+
{
|
|
227
|
+
throw new ArgumentOutOfRangeException(
|
|
228
|
+
nameof(options),
|
|
229
|
+
options.TimeToLive,
|
|
230
|
+
$"eval-cache-ttl-too-short: o tempo de vida das entradas e {options.TimeToLive.TotalDays:0} dias, "
|
|
231
|
+
+ $"abaixo do piso de {EvalCacheOptions.MinimumTimeToLive.TotalDays:0}. "
|
|
232
|
+
+ "A expiracao da cache e DESTRUTIVA NA LEITURA: uma entrada vencida faz o provedor apagar o "
|
|
233
|
+
+ "diretorio da chave antes de devolver nulo. Numa arvore commitada isso significa miss em massa "
|
|
234
|
+
+ "(e git status sujo) num dia em que ninguem mexeu em nada.");
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
var clock = timeProvider ?? TimeProvider.System;
|
|
238
|
+
IReadOnlyList<IEvaluator> quality = qualityEvaluators is null ? [] : [.. qualityEvaluators];
|
|
239
|
+
|
|
240
|
+
// O carimbo so e COBRADO nos modos que LEEM a cache. Em Refresh a arvore esta sendo
|
|
241
|
+
// (re)escrita — cobrar carimbo ali tornaria impossivel criar a primeira cache.
|
|
242
|
+
var stampCheck = options.Mode == EvalMode.Refresh || options.Pin is null
|
|
243
|
+
? new EvalCacheStampCheck(
|
|
244
|
+
EvalStampVerdict.Ok,
|
|
245
|
+
options.Pin is null ? "sem pin declarado" : "modo refresh: o carimbo sera reescrito")
|
|
246
|
+
: EvalCacheStamp.Check(options.CacheRoot, options.Pin, clock);
|
|
247
|
+
|
|
248
|
+
if (!stampCheck.IsOk)
|
|
249
|
+
{
|
|
250
|
+
throw new InvalidOperationException(stampCheck.Reason);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
var cursor = new ScenarioCursor();
|
|
254
|
+
OfflineChatClient? offline = null;
|
|
255
|
+
CountingChatClient? counting = null;
|
|
256
|
+
IChatClient inner;
|
|
257
|
+
|
|
258
|
+
if (options.Mode == EvalMode.Offline)
|
|
259
|
+
{
|
|
260
|
+
offline = new OfflineChatClient(
|
|
261
|
+
options.ProviderName,
|
|
262
|
+
options.DefaultModelId,
|
|
263
|
+
options.CacheRoot,
|
|
264
|
+
options.EnvFile,
|
|
265
|
+
() => cursor.Name);
|
|
266
|
+
inner = offline;
|
|
267
|
+
}
|
|
268
|
+
else
|
|
269
|
+
{
|
|
270
|
+
counting = new CountingChatClient(
|
|
271
|
+
liveClient ?? throw new InvalidOperationException(
|
|
272
|
+
$"eval-live-client-missing: o modo {options.Mode} exige um IChatClient real. "
|
|
273
|
+
+ "Sem ele nao ha o que gravar na cache — e inventar um cliente aqui produziria uma arvore "
|
|
274
|
+
+ "de cache que nao corresponde a provider nenhum."));
|
|
275
|
+
inner = counting;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return new EvalFixture(options, stampCheck, cursor, inner, offline, counting, quality);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/// <summary>
|
|
282
|
+
/// Roda TODOS os cenarios do dataset e escreve o sumario, quando ha caminho para ele.
|
|
283
|
+
/// </summary>
|
|
284
|
+
/// <exception cref="InvalidOperationException">Dataset sem nenhum cenario — uma suite de eval que avalia zero cenarios e sai verde e um guardrail inerte.</exception>
|
|
285
|
+
public async Task<EvalRunSummary> RunAllAsync(CancellationToken cancellationToken = default)
|
|
286
|
+
{
|
|
287
|
+
var scenarios = EvalScenario.LoadAll(Options.DatasetRoot);
|
|
288
|
+
if (scenarios.Count == 0)
|
|
289
|
+
{
|
|
290
|
+
throw new InvalidOperationException(
|
|
291
|
+
$"eval-empty-run: o dataset em \"{Options.DatasetRoot}\" nao tem nenhum cenario. "
|
|
292
|
+
+ "Uma suite de eval que avalia zero cenarios e sai verde e exatamente o guardrail inerte "
|
|
293
|
+
+ "que este kit existe para nao produzir.");
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Em Refresh a arvore inteira e zerada antes de regravar: uma regravacao parcial
|
|
297
|
+
// deixaria entradas de uma versao antiga convivendo com entradas novas, e a proxima
|
|
298
|
+
// leitura offline nao teria como saber quais sao quais.
|
|
299
|
+
if (Options.Mode == EvalMode.Refresh)
|
|
300
|
+
{
|
|
301
|
+
try
|
|
302
|
+
{
|
|
303
|
+
await new ShortPathResponseCacheProvider(Options.CacheRoot, Options.TimeToLive)
|
|
304
|
+
.ResetAsync(cancellationToken).ConfigureAwait(false);
|
|
305
|
+
}
|
|
306
|
+
catch (DirectoryNotFoundException)
|
|
307
|
+
{
|
|
308
|
+
// MEDIDO na v10.9.0: ResetAsync chama Directory.Delete no subdiretorio de
|
|
309
|
+
// cache e LANCA quando ele ainda nao existe — o caso da PRIMEIRA gravacao.
|
|
310
|
+
// "Nao ha o que zerar" e sucesso, nao falha. Capturado aqui, e nao evitado
|
|
311
|
+
// com um Directory.Exists sobre o layout interno da biblioteca: repetir o
|
|
312
|
+
// nome do subdiretorio dela aqui seria uma segunda verdade sobre onde a
|
|
313
|
+
// cache mora.
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
foreach (var scenario in scenarios)
|
|
318
|
+
{
|
|
319
|
+
await RunAsync(scenario, cancellationToken).ConfigureAwait(false);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (Options.Mode == EvalMode.Refresh && Options.Pin is not null)
|
|
323
|
+
{
|
|
324
|
+
EvalCacheStamp.Write(Options.CacheRoot, new EvalCacheStamp
|
|
325
|
+
{
|
|
326
|
+
PinnedAt = Options.Pin.PinnedAt,
|
|
327
|
+
Packages = Options.Pin.Packages,
|
|
328
|
+
Provider = Options.ProviderName,
|
|
329
|
+
Model = Options.DefaultModelId,
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
var summary = Summary;
|
|
334
|
+
if (!string.IsNullOrWhiteSpace(Options.SummaryPath))
|
|
335
|
+
{
|
|
336
|
+
summary.WriteTo(Options.SummaryPath);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return summary;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/// <summary>Roda UM cenario: resolve a resposta (cache ou cliente interno) e aplica os avaliadores.</summary>
|
|
343
|
+
public async Task<EvalScenarioResult> RunAsync(EvalScenario scenario, CancellationToken cancellationToken = default)
|
|
344
|
+
{
|
|
345
|
+
ArgumentNullException.ThrowIfNull(scenario);
|
|
346
|
+
|
|
347
|
+
_cursor.Name = scenario.CacheKey;
|
|
348
|
+
|
|
349
|
+
var evaluators = new List<IEvaluator>(scenario.Assertions);
|
|
350
|
+
evaluators.AddRange(_quality);
|
|
351
|
+
|
|
352
|
+
var configuration = new ReportingConfiguration(
|
|
353
|
+
evaluators: evaluators,
|
|
354
|
+
resultStore: _resultStore,
|
|
355
|
+
chatConfiguration: new ChatConfiguration(_inner),
|
|
356
|
+
responseCacheProvider: new ShortPathResponseCacheProvider(Options.CacheRoot, Options.TimeToLive),
|
|
357
|
+
executionName: ExecutionName);
|
|
358
|
+
|
|
359
|
+
await using var run = await configuration
|
|
360
|
+
.CreateScenarioRunAsync(scenario.CacheKey, Defaults.DefaultIterationName, cancellationToken: cancellationToken)
|
|
361
|
+
.ConfigureAwait(false);
|
|
362
|
+
|
|
363
|
+
var chatClient = run.ChatConfiguration?.ChatClient
|
|
364
|
+
?? throw new InvalidOperationException(
|
|
365
|
+
"eval-chat-client-missing: o ScenarioRun nao expos um chat client — sem ele nao ha camada de cache.");
|
|
366
|
+
|
|
367
|
+
var response = await chatClient
|
|
368
|
+
.GetResponseAsync(scenario.Messages, cancellationToken: cancellationToken)
|
|
369
|
+
.ConfigureAwait(false);
|
|
370
|
+
|
|
371
|
+
var result = await run
|
|
372
|
+
.EvaluateAsync(scenario.Messages, response, cancellationToken: cancellationToken)
|
|
373
|
+
.ConfigureAwait(false);
|
|
374
|
+
|
|
375
|
+
var outcomes = new List<EvalAssertionOutcome>(scenario.Assertions.Count);
|
|
376
|
+
foreach (var assertion in scenario.Assertions)
|
|
377
|
+
{
|
|
378
|
+
outcomes.Add(assertion.Evaluate(response));
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Um cenario SEM assercao deterministica nao passa: ele nao cobra nada. Deixa-lo
|
|
382
|
+
// verde por vacuidade e o mesmo defeito de um teste vazio — e aqui ele seria pior,
|
|
383
|
+
// porque o cenario apareceria na contagem como se tivesse verificado algo.
|
|
384
|
+
var scenarioResult = new EvalScenarioResult(
|
|
385
|
+
scenario.CacheKey,
|
|
386
|
+
outcomes.Count > 0 && outcomes.All(o => o.Passed),
|
|
387
|
+
outcomes,
|
|
388
|
+
result);
|
|
389
|
+
|
|
390
|
+
_results.Add(scenarioResult);
|
|
391
|
+
return scenarioResult;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/// <inheritdoc />
|
|
395
|
+
public ValueTask DisposeAsync()
|
|
396
|
+
{
|
|
397
|
+
_inner.Dispose();
|
|
398
|
+
return ValueTask.CompletedTask;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
private IReadOnlyDictionary<string, double> CollectQuality()
|
|
402
|
+
{
|
|
403
|
+
var names = new HashSet<string>(_quality.SelectMany(q => q.EvaluationMetricNames), StringComparer.Ordinal);
|
|
404
|
+
var acc = new Dictionary<string, double>(StringComparer.Ordinal);
|
|
405
|
+
if (names.Count == 0 || _results.Count == 0)
|
|
406
|
+
{
|
|
407
|
+
return acc;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
foreach (var result in _results)
|
|
411
|
+
{
|
|
412
|
+
foreach (var (name, metric) in result.Result.Metrics)
|
|
413
|
+
{
|
|
414
|
+
if (names.Contains(name) && metric is NumericMetric { Value: not null } numeric)
|
|
415
|
+
{
|
|
416
|
+
acc[name] = acc.TryGetValue(name, out var sum) ? sum + numeric.Value.Value : numeric.Value.Value;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
foreach (var name in acc.Keys.ToList())
|
|
422
|
+
{
|
|
423
|
+
acc[name] = Math.Round(acc[name] / _results.Count, 3);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
return acc;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
private static string Required(string variable)
|
|
430
|
+
=> Environment.GetEnvironmentVariable(variable)
|
|
431
|
+
?? throw new InvalidOperationException(
|
|
432
|
+
$"eval-env-missing: a variavel {variable} nao esta definida. "
|
|
433
|
+
+ "Ela e DITADA pelo CLI (morph-spec verify); rodar a suite de eval a mao exige defini-la.");
|
|
434
|
+
|
|
435
|
+
private static EvalMode ParseMode(string? raw) => (raw ?? "offline").ToLowerInvariant() switch
|
|
436
|
+
{
|
|
437
|
+
"offline" => EvalMode.Offline,
|
|
438
|
+
"refresh" => EvalMode.Refresh,
|
|
439
|
+
"online" => EvalMode.Online,
|
|
440
|
+
_ => throw new InvalidOperationException(
|
|
441
|
+
$"eval-mode-unknown: MORPH_EVAL_MODE=\"{raw}\". Conhecidos: offline, refresh, online."),
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
private static EvalPin? ParsePin(string? raw)
|
|
445
|
+
{
|
|
446
|
+
if (string.IsNullOrWhiteSpace(raw))
|
|
447
|
+
{
|
|
448
|
+
return null;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
try
|
|
452
|
+
{
|
|
453
|
+
return System.Text.Json.JsonSerializer.Deserialize<EvalPin>(raw);
|
|
454
|
+
}
|
|
455
|
+
catch (System.Text.Json.JsonException e)
|
|
456
|
+
{
|
|
457
|
+
throw new InvalidOperationException(
|
|
458
|
+
$"eval-pin-unreadable: MORPH_EVAL_PIN nao parseia ({e.Message}). "
|
|
459
|
+
+ "Ela e escrita pelo CLI a partir de framework/ai-pin.json — um valor ilegivel aqui e defeito do CLI, "
|
|
460
|
+
+ "e trata-lo como 'sem pin' desligaria a checagem de carimbo em silencio.");
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
namespace Morph.AiKit.Evals;
|
|
2
|
+
|
|
3
|
+
/// <summary>
|
|
4
|
+
/// De onde a resposta do modelo pode vir nesta execucao de eval.
|
|
5
|
+
/// </summary>
|
|
6
|
+
/// <remarks>
|
|
7
|
+
/// A distincao existe porque as tres situacoes tem custos diferentes em DINHEIRO e em
|
|
8
|
+
/// confianca, e um enum de dois valores ("usa cache" / "nao usa") esconderia a do meio.
|
|
9
|
+
/// </remarks>
|
|
10
|
+
public enum EvalMode
|
|
11
|
+
{
|
|
12
|
+
/// <summary>
|
|
13
|
+
/// So a cache. Um miss e falha nomeada, nunca uma chamada de rede — e nunca um
|
|
14
|
+
/// <c>skip</c>: gate verde sobre verificacao que nao aconteceu e o modo de falha que
|
|
15
|
+
/// este harness mais paga para evitar. E o modo do CI.
|
|
16
|
+
/// </summary>
|
|
17
|
+
Offline = 0,
|
|
18
|
+
|
|
19
|
+
/// <summary>
|
|
20
|
+
/// Reexecuta TODOS os cenarios contra o provider e regrava a arvore de cache. Exige
|
|
21
|
+
/// credencial e nunca acontece implicitamente: uma regravacao automatica e uma chamada
|
|
22
|
+
/// paga que ninguem pediu.
|
|
23
|
+
/// </summary>
|
|
24
|
+
Refresh = 1,
|
|
25
|
+
|
|
26
|
+
/// <summary>
|
|
27
|
+
/// Cache primeiro, provider no miss. E o modo de quem esta escrevendo cenario novo na
|
|
28
|
+
/// propria maquina: paga so pelo que falta, e o resultado sai marcado como
|
|
29
|
+
/// <c>cold</c> com aviso de que a arvore mudou e precisa ser commitada.
|
|
30
|
+
/// </summary>
|
|
31
|
+
Online = 2,
|
|
32
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
using Microsoft.Extensions.AI.Evaluation;
|
|
2
|
+
using Microsoft.Extensions.AI.Evaluation.Quality;
|
|
3
|
+
|
|
4
|
+
namespace Morph.AiKit.Evals;
|
|
5
|
+
|
|
6
|
+
/// <summary>
|
|
7
|
+
/// Os avaliadores de JUIZO que o kit considera utilizaveis, e a fronteira que os separa dos
|
|
8
|
+
/// experimentais.
|
|
9
|
+
/// </summary>
|
|
10
|
+
/// <remarks>
|
|
11
|
+
/// <para>
|
|
12
|
+
/// <b>Eles nunca decidem.</b> O veredito de um cenario sai das <see cref="EvalAssertion"/>
|
|
13
|
+
/// deterministicas; o que sai daqui e METRICA REPORTADA. Um juizo de LLM que reprovasse
|
|
14
|
+
/// sozinho reintroduziria a variancia que faz suites de eval serem desligadas do CI — e um
|
|
15
|
+
/// no de eval desligado nao verifica nada.
|
|
16
|
+
/// </para>
|
|
17
|
+
/// <para>
|
|
18
|
+
/// <b>Por que a lista e curta.</b> Dois dos quatro avaliadores que o plano desta feature
|
|
19
|
+
/// listava como estaveis carregam <c>[Experimental("AIEVAL001")]</c> na versao pinada:
|
|
20
|
+
/// <c>TaskAdherenceEvaluator</c> e <c>ToolCallAccuracyEvaluator</c>. Sob
|
|
21
|
+
/// <c>TreatWarningsAsErrors</c>, usa-los exige suprimir um diagnostico — uma decisao
|
|
22
|
+
/// consciente de quem adota o kit, jamais um efeito colateral de chamar um metodo chamado
|
|
23
|
+
/// "Stable". Medido por reflexao sobre <c>Microsoft.Extensions.AI.Evaluation.Quality</c>
|
|
24
|
+
/// 10.9.0, e pinado pelo teste <c>WhichQualityEvaluatorsAreExperimentalIsMEASURED</c>.
|
|
25
|
+
/// </para>
|
|
26
|
+
/// <para>
|
|
27
|
+
/// <b>E por que este tipo existe.</b> Sem uma unica referencia em <c>src/</c>, o pacote
|
|
28
|
+
/// <c>.Quality</c> teria apenas contrato de VERSAO (o restore, NU1102) e um rename de API
|
|
29
|
+
/// nele nao reprovaria PR nenhuma. Com o <c>using</c> real acima, ele passa a ter cobertura
|
|
30
|
+
/// de compilacao — que e o nivel de garantia que o kit promete para a familia de Evaluation.
|
|
31
|
+
/// </para>
|
|
32
|
+
/// </remarks>
|
|
33
|
+
public static class EvalQualityEvaluators
|
|
34
|
+
{
|
|
35
|
+
/// <summary>
|
|
36
|
+
/// Os avaliadores de qualidade NAO experimentais da versao pinada, prontos para
|
|
37
|
+
/// <see cref="EvalFixture.Create"/>.
|
|
38
|
+
/// </summary>
|
|
39
|
+
/// <remarks>
|
|
40
|
+
/// Uma lista nova a cada chamada: os avaliadores sao passados adiante e guardados por uma
|
|
41
|
+
/// <c>ReportingConfiguration</c>, e um array compartilhado seria estado global disfarcado
|
|
42
|
+
/// de conveniencia.
|
|
43
|
+
/// </remarks>
|
|
44
|
+
public static IReadOnlyList<IEvaluator> Stable() =>
|
|
45
|
+
[
|
|
46
|
+
new RelevanceEvaluator(),
|
|
47
|
+
new CompletenessEvaluator(),
|
|
48
|
+
];
|
|
49
|
+
}
|