@polymorphism-tech/morph-spec 8.38.0 → 8.41.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +20 -9
- package/README.md +2 -2
- package/bin/morph-spec.js +60 -43
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +4 -3
- package/framework/MORPH.md +10 -9
- package/framework/agents.json +43 -26
- package/framework/docs/CLI.md +7 -5
- package/framework/evals/rubrics/code-quality.md +4 -4
- package/framework/evals/rubrics/condensed.md +2 -2
- package/framework/hooks/README.md +1 -1
- package/framework/hooks/claude-code/core/telemetry-log.js +39 -8
- package/framework/hooks/claude-code/core/worktree-create.js +22 -6
- package/framework/hooks/shared/git-paths.js +38 -5
- package/framework/mcp/registry.json +58 -66
- package/framework/rules/infrastructure-standards.md +0 -1
- package/framework/rules/morph-workflow.md +9 -4
- package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
- package/framework/skills/core/morph-apply/SKILL.md +59 -25
- package/framework/skills/core/morph-brainstorming/SKILL.md +1 -1
- package/framework/skills/core/morph-eval/SKILL.md +1 -1
- package/framework/skills/core/morph-hotfix/SKILL.md +2 -2
- package/framework/skills/core/morph-implement/SKILL.md +48 -24
- package/framework/skills/core/morph-media/SKILL.md +2 -2
- package/framework/skills/core/morph-plan/SKILL.md +10 -3
- package/framework/skills/core/morph-proposal/SKILL.md +1 -1
- package/framework/skills/core/morph-review/SKILL.md +1 -1
- package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
- package/framework/skills/core/morph-standards/SKILL.md +1 -1
- package/framework/skills/core/morph-uiux/SKILL.md +7 -6
- package/framework/standards/PROVENANCE.json +4 -4
- package/framework/standards/STANDARDS.json +9 -9
- package/framework/standards/ai-agents/context-providers.md +16 -26
- package/framework/standards/ai-agents/durable-workflows-hitl.md +15 -21
- package/framework/standards/ai-agents/modalities-image-gen.md +21 -25
- package/framework/standards/ai-agents/modalities-speech-to-text.md +4 -15
- package/framework/standards/ai-agents/modalities-text-to-speech.md +2 -11
- package/framework/standards/ai-agents/modalities-vision.md +1 -18
- package/framework/standards/ai-agents/prompt-sources.md +0 -4
- package/framework/standards/architecture/code-comments.md +52 -16
- package/framework/standards/backend/dotnet/testing.md +4 -2
- package/framework/standards/backend/integrations/neon-auth/neon-auth.md +63 -50
- package/framework/standards/frontend/design-system/ai-image-generation.md +42 -28
- package/framework/standards/frontend/design-system/ai-video-generation.md +24 -9
- package/framework/standards/frontend/nextjs/testing.md +4 -2
- package/framework/standards/integration/mcp/mcp-tools.md +64 -126
- package/framework/templates/code/dotnet/abstractions/result.cs +0 -2
- package/framework/templates/code/dotnet/ai-agents/AgentBootstrap.cs.template +26 -39
- package/framework/templates/code/dotnet/ai-agents/EmbeddingService.cs.template +28 -68
- package/framework/templates/code/dotnet/ai-agents/ModelRegistry.cs.template +99 -117
- package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +2 -44
- package/framework/templates/code/dotnet/ai-agents/ResilientToolMiddleware.cs.template +40 -61
- package/framework/templates/code/dotnet/ai-agents/VectorSearchTool.cs.template +54 -107
- package/framework/templates/code/dotnet/ai-agents/model-registry.json.template +53 -58
- package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +192 -281
- package/framework/templates/code/dotnet/database/migration.cs +49 -83
- package/framework/templates/code/dotnet/database/vector-entity.cs +2 -46
- package/framework/templates/code/dotnet/extensions/result-extensions.cs +0 -2
- package/framework/templates/code/dotnet/feature/validator.cs.hbs +11 -15
- package/framework/templates/code/dotnet/jobs/agent.cs +3 -51
- package/framework/templates/code/dotnet/jobs/job.cs +2 -97
- package/framework/templates/code/dotnet/test.cs +2 -87
- package/framework/templates/code/typescript/contracts.ts +140 -167
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentApi.cs +1 -48
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactory.cs +3 -79
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactoryOptions.cs +1 -70
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpec.cs +1 -47
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpecOptions.cs +2 -99
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/AiKitAssemblyMarker.cs +0 -6
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/ConversationStore.cs +3 -88
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/IConversationStore.cs +1 -41
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/InMemoryConversationStore.cs +0 -21
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/CountingChatClient.cs +1 -25
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalAssertion.cs +2 -63
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheMissException.cs +0 -30
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheOptions.cs +1 -52
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheStamp.cs +3 -78
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalFixture.cs +4 -109
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalMode.cs +0 -22
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalQualityEvaluators.cs +1 -36
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalScenario.cs +2 -57
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalSummary.cs +0 -75
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/OfflineChatClient.cs +2 -59
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/ShortPathResponseCache.cs +4 -111
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaGenerator.cs +0 -34
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaProvider.cs +1 -57
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaArtifact.cs +0 -19
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaDisclosure.cs +2 -74
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaGenerator.cs +2 -33
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaIdempotencyKey.cs +1 -43
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJob.cs +1 -96
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobId.cs +0 -13
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobState.cs +0 -27
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaPricing.cs +0 -33
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaProvenance.cs +0 -31
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaRequest.cs +3 -75
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaResult.cs +0 -42
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaReview.cs +0 -39
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaSeams.cs +1 -55
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaStore.cs +3 -59
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MorphMedia.cs +0 -46
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/GoogleImageProvider.cs +3 -28
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/OpenAIImageProvider.cs +7 -61
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/VeoVideoProvider.cs +2 -37
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/IUsageCostSink.cs +1 -23
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostMiddleware.cs +2 -82
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostRecord.cs +1 -68
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphOpenTelemetryExtensions.cs +1 -62
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphTelemetryOptions.cs +2 -62
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/IProviderClientFactory.cs +1 -25
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelAlias.cs +0 -124
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelRegistry.cs +1 -73
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaIngest.cs +1 -66
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaPlan.cs +0 -41
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentFactoryTests.cs +2 -112
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentSpecTests.cs +2 -43
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ConversationStoreTests.cs +5 -110
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/DeltaIngestTests.cs +7 -103
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalAssertionTests.cs +1 -42
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheMissTests.cs +1 -30
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheStampTests.cs +1 -19
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFailuresTests.cs +0 -48
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFixtureTests.cs +1 -29
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalRefreshTests.cs +2 -38
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalWorkspace.cs +1 -14
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/KitEvals.cs +4 -69
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/OfflineChatClientTests.cs +1 -10
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClient.cs +1 -57
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClientTests.cs +0 -26
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeClock.cs +1 -19
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeMediaProvider.cs +0 -32
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeProviderClientFactory.cs +0 -14
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeUsageCostSink.cs +0 -24
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/InMemoryMediaStore.cs +0 -32
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAccessibilityTests.cs +0 -37
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAliasIsNotAnAgentTests.cs +0 -24
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaDisclosureTests.cs +0 -78
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaGeneratorTests.cs +0 -53
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaIdempotencyKeyTests.cs +0 -44
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaJobStateTests.cs +0 -44
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaReviewTests.cs +0 -19
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaStoreTests.cs +0 -24
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ModelRegistryTests.cs +6 -48
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/SkeletonSmokeTests.cs +1 -13
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/TelemetryTests.cs +2 -71
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/UsageCostMiddlewareTests.cs +5 -100
- package/framework/templates/frontend/nextjs/Dockerfile.nextjs.hbs +39 -43
- package/framework/templates/frontend/nextjs/env.mjs.hbs +29 -32
- package/framework/templates/frontend/nextjs/page.tsx.hbs +21 -22
- package/framework/templates/frontend/nextjs/use-feature.ts.hbs +42 -54
- package/framework/templates/integrations/asaas-client.cs +332 -387
- package/framework/templates/integrations/asaas-webhook.cs +290 -351
- package/framework/templates/integrations/neon-auth-config.cs +9 -105
- package/framework/templates/project/validate.js +97 -120
- package/package.json +2 -2
- package/src/commands/dashboard/dashboard.js +80 -53
- package/src/commands/mcp/mcp-setup.js +37 -9
- package/src/commands/project/doctor.js +12 -6
- package/src/commands/project/init.js +6 -3
- package/src/commands/project/install-plugin-cmd.js +1 -1
- package/src/commands/project/update.js +5 -0
- package/src/commands/project/worktree.js +60 -4
- package/src/commands/state/gate-check.js +5 -0
- package/src/commands/tasks/dag.js +27 -4
- package/src/commands/validation/verify.js +22 -38
- package/src/dashboard/app/App.jsx +80 -16
- package/src/dashboard/app/components/ActivityFeed.jsx +172 -0
- package/src/dashboard/app/components/CommandPalette.jsx +155 -0
- package/src/dashboard/app/components/DetailPanel.jsx +47 -11
- package/src/dashboard/app/components/EntityLink.jsx +55 -0
- package/src/dashboard/app/components/ErrorBoundary.jsx +54 -0
- package/src/dashboard/app/components/FlowGraph.jsx +19 -17
- package/src/dashboard/app/components/KnowledgeMarkdown.jsx +53 -0
- package/src/dashboard/app/components/Markdown.jsx +47 -0
- package/src/dashboard/app/components/PhaseRail.jsx +111 -0
- package/src/dashboard/app/components/SearchFilter.jsx +40 -22
- package/src/dashboard/app/components/Sidebar.jsx +146 -37
- package/src/dashboard/app/hooks/useMorphQuery.js +54 -0
- package/src/dashboard/app/hooks/usePref.js +38 -0
- package/src/dashboard/app/lib/format.js +69 -0
- package/src/dashboard/app/lib/phases.js +56 -0
- package/src/dashboard/app/lib/project.js +50 -0
- package/src/dashboard/app/pages/Activity.jsx +24 -0
- package/src/dashboard/app/pages/AgentMap.jsx +371 -370
- package/src/dashboard/app/pages/FeatureDetail.jsx +604 -0
- package/src/dashboard/app/pages/Features.jsx +400 -0
- package/src/dashboard/app/pages/Guide.jsx +146 -0
- package/src/dashboard/app/pages/HookInspector.jsx +357 -322
- package/src/dashboard/app/pages/PhasePipeline.jsx +277 -408
- package/src/dashboard/app/pages/Portfolio.jsx +156 -0
- package/src/dashboard/app/pages/Rubrics.jsx +101 -0
- package/src/dashboard/app/pages/RulesMap.jsx +300 -273
- package/src/dashboard/app/pages/SkillsLibrary.jsx +483 -366
- package/src/dashboard/app/pages/Standards.jsx +407 -332
- package/src/dashboard/app/styles/index.css +187 -47
- package/src/dashboard/index.html +14 -12
- package/src/dashboard/package.json +32 -24
- package/src/dashboard/scanner/activity-scanner.js +351 -0
- package/src/dashboard/scanner/agents-scanner.js +59 -1
- package/src/dashboard/scanner/docs-scanner.js +59 -0
- package/src/dashboard/scanner/feature-status.js +10 -0
- package/src/dashboard/scanner/features-scanner.js +360 -0
- package/src/dashboard/scanner/index.js +14 -7
- package/src/dashboard/scanner/phases-scanner.js +14 -15
- package/src/dashboard/scanner/portfolio.js +153 -0
- package/src/dashboard/scanner/project-api.js +195 -0
- package/src/dashboard/scanner/rules-scanner.js +42 -37
- package/src/dashboard/scanner/skills-scanner.js +105 -43
- package/src/dashboard/scanner/standards-scanner.js +2 -0
- package/src/dashboard/vite-plugin-morph-data.js +134 -17
- package/src/lib/detectors/claude-config-detector.js +2 -7
- package/src/lib/detectors/index.js +5 -1
- package/src/lib/evaluator-report.js +22 -0
- package/src/lib/generators/settings-generator.js +208 -209
- package/src/lib/installers/mcp-installer.js +724 -719
- package/src/lib/installers/memory-installer.js +1 -2
- package/src/lib/projects/registry.js +154 -0
- package/src/lib/risk-detector.js +13 -0
- package/src/lib/tasks/tasks-json-parser.js +21 -0
- package/src/lib/validators/code-budget/code-budget-validator.js +32 -4
- package/src/lib/worktree/env-allocator.js +9 -3
- package/src/scripts/install-plugin.js +1 -1
- package/src/scripts/setup-infra.js +2 -4
- package/src/utils/agents-installer.js +108 -13
- package/src/utils/banner.js +1 -1
- package/src/commands/index.js +0 -13
- package/src/commands/project/checkpoint.js +0 -209
- package/src/core/state/phase-state-machine.js +0 -197
- package/src/dashboard/app/hooks/useDetailPanel.js +0 -15
- package/src/dashboard/app/pages/LiveState.jsx +0 -206
- package/src/dashboard/scanner/squads-scanner.js +0 -13
- package/src/dashboard/scanner/state-scanner.js +0 -85
- package/src/lib/generators/index.js +0 -10
- package/src/lib/hooks/hook-executor.js +0 -167
- package/src/lib/validators/checkpoint-persistence.js +0 -39
- package/src/lib/validators/shared/emit-validator-dispatch.js +0 -14
- package/src/lib/validators/shared/index.js +0 -12
- package/src/lib/validators/shared/issue-counter.js +0 -14
- package/src/lib/validators/shared/result-formatter.js +0 -124
package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphTelemetryOptions.cs
CHANGED
|
@@ -1,31 +1,10 @@
|
|
|
1
1
|
namespace Morph.AiKit.Observability;
|
|
2
2
|
|
|
3
|
-
/// <summary>
|
|
4
|
-
/// O que o kit precisa saber para emitir telemetria GenAI: sob que <c>ActivitySource</c>,
|
|
5
|
-
/// com ou sem conteudo sensivel, e — a decisao que este tipo existe para carregar — se a
|
|
6
|
-
/// instrumentacao do chat client deve ser aplicada.
|
|
7
|
-
/// </summary>
|
|
8
|
-
/// <remarks>
|
|
9
|
-
/// <para>
|
|
10
|
-
/// <b>Por que <see cref="SourceName"/> e obrigatorio.</b> O standard
|
|
11
|
-
/// <c>ai-agents-observability-patterns</c> manda usar
|
|
12
|
-
/// <c>AgentOpenTelemetryConsts.DefaultSourceName</c>. No <c>Microsoft.Agents.AI</c> 1.20.0
|
|
13
|
-
/// esse tipo <b>nao e publico</b> (apurado por reflexao sobre o assembly do pin: a classe
|
|
14
|
-
/// se chama <c>OpenTelemetryConsts</c> e e <c>internal</c>). Um kit que quisesse "usar a
|
|
15
|
-
/// constante do MAF" so poderia repetir o texto dela — e um literal copiado envelhece em
|
|
16
|
-
/// silencio na primeira vez que o pacote o mudar. Entao o nome e do projeto: e a MESMA
|
|
17
|
-
/// string que ele passa para <c>AddSource(...)</c> na configuracao do OpenTelemetry, e o
|
|
18
|
-
/// span do agente e o span de chat nascem sob ela, juntos.
|
|
19
|
-
/// </para>
|
|
20
|
-
/// </remarks>
|
|
21
3
|
public sealed record MorphTelemetryOptions
|
|
22
4
|
{
|
|
23
5
|
private readonly string _sourceName = string.Empty;
|
|
24
6
|
|
|
25
|
-
|
|
26
|
-
/// Nome do <c>ActivitySource</c> dos spans deste kit. Branco e rejeitado na construcao:
|
|
27
|
-
/// um source sem nome e telemetria que ninguem consegue registrar nem coletar.
|
|
28
|
-
/// </summary>
|
|
7
|
+
// AgentOpenTelemetryConsts e internal no Microsoft.Agents.AI 1.20.0, entao o nome e do projeto.
|
|
29
8
|
public required string SourceName
|
|
30
9
|
{
|
|
31
10
|
get => _sourceName;
|
|
@@ -36,47 +15,8 @@ public sealed record MorphTelemetryOptions
|
|
|
36
15
|
}
|
|
37
16
|
}
|
|
38
17
|
|
|
39
|
-
/// <summary>
|
|
40
|
-
/// Se prompt, resposta, argumentos e resultados de tool entram nos spans. <c>false</c>
|
|
41
|
-
/// por padrao — e o standard exige <c>false</c> fora de dev/staging, porque esse
|
|
42
|
-
/// conteudo e PII do cliente.
|
|
43
|
-
/// </summary>
|
|
44
18
|
public bool EnableSensitiveData { get; init; }
|
|
45
19
|
|
|
46
|
-
|
|
47
|
-
/// Se o pipeline de chat deve receber instrumentacao propria. <b>Tres valores, e o
|
|
48
|
-
/// default e o guard.</b>
|
|
49
|
-
/// </summary>
|
|
50
|
-
/// <remarks>
|
|
51
|
-
/// <list type="table">
|
|
52
|
-
/// <listheader><term>Valor</term><description>O que faz</description></listheader>
|
|
53
|
-
/// <item>
|
|
54
|
-
/// <term><c>null</c> (default)</term>
|
|
55
|
-
/// <description>
|
|
56
|
-
/// <b>Instrumenta somente se ainda nao houver instrumentacao no pipeline.</b> A
|
|
57
|
-
/// deteccao e <c>GetService(typeof(OpenTelemetryChatClient))</c>, que atravessa a
|
|
58
|
-
/// cadeia de delegates (medido). E este ramo, e so ele, que impede o span de chat
|
|
59
|
-
/// duplicado.
|
|
60
|
-
/// </description>
|
|
61
|
-
/// </item>
|
|
62
|
-
/// <item>
|
|
63
|
-
/// <term><c>true</c></term>
|
|
64
|
-
/// <description>
|
|
65
|
-
/// Instrumenta sempre. Sobre um pipeline ja instrumentado, produz <b>dois</b>
|
|
66
|
-
/// spans <c>chat</c> por invocacao — medido, e pinado por teste. Existe para
|
|
67
|
-
/// documentar o risco, nao para ser ligado.
|
|
68
|
-
/// </description>
|
|
69
|
-
/// </item>
|
|
70
|
-
/// <item>
|
|
71
|
-
/// <term><c>false</c></term>
|
|
72
|
-
/// <description>
|
|
73
|
-
/// Nunca instrumenta. Com a <c>AgentFactory</c>, que monta o proprio pipeline
|
|
74
|
-
/// (<c>UseProvidedChatClientAsIs = true</c>), isso significa <b>nenhum</b> span de
|
|
75
|
-
/// <c>chat</c>: o slot que o MAF reservaria e ativaria sozinho nao existe nesse
|
|
76
|
-
/// arranjo. Escolha consciente, nao default.
|
|
77
|
-
/// </description>
|
|
78
|
-
/// </item>
|
|
79
|
-
/// </list>
|
|
80
|
-
/// </remarks>
|
|
20
|
+
// null instrumenta so se o pipeline ainda nao tem OpenTelemetryChatClient; true duplica o span de chat.
|
|
81
21
|
public bool? InstrumentChatClient { get; init; }
|
|
82
22
|
}
|
package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/IProviderClientFactory.cs
CHANGED
|
@@ -2,32 +2,8 @@ using Microsoft.Extensions.AI;
|
|
|
2
2
|
|
|
3
3
|
namespace Morph.AiKit.Providers;
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
/// Constroi o <see cref="IChatClient"/> de um alias. E <b>a</b> costura do kit: o unico
|
|
7
|
-
/// ponto em que o codigo toca um provider de verdade.
|
|
8
|
-
/// </summary>
|
|
9
|
-
/// <remarks>
|
|
10
|
-
/// <para>
|
|
11
|
-
/// O <see cref="ModelRegistry"/> nao sabe construir cliente de provider nenhum — de
|
|
12
|
-
/// proposito. Quem sabe e uma implementacao desta interface, e e por isso que a suite do
|
|
13
|
-
/// kit roda sem rede e sem chave de API: em teste, a fabrica devolve
|
|
14
|
-
/// <c>FakeChatClient</c>. Num projeto, ela e o <c>switch</c> por
|
|
15
|
-
/// <see cref="ModelAlias.Provider"/> descrito no standard
|
|
16
|
-
/// <c>ai-agents-providers-model-registry</c> (<c>openai</c> e <c>ollama</c> de saida,
|
|
17
|
-
/// os demais como branch nova).
|
|
18
|
-
/// </para>
|
|
19
|
-
/// <para>
|
|
20
|
-
/// <b>Quem descarta o cliente:</b> o <see cref="ModelRegistry"/>. A fabrica cria; o
|
|
21
|
-
/// registry memoiza por alias e descarta no proprio <c>Dispose</c>. Uma fabrica que
|
|
22
|
-
/// guardasse e reaproveitasse clientes por fora criaria uma segunda cache, e duas caches
|
|
23
|
-
/// da mesma coisa e como um cliente vazado sobrevive a um teste.
|
|
24
|
-
/// </para>
|
|
25
|
-
/// </remarks>
|
|
5
|
+
// O ModelRegistry memoiza e descarta o que a fabrica cria; a fabrica nao guarda cliente.
|
|
26
6
|
public interface IProviderClientFactory
|
|
27
7
|
{
|
|
28
|
-
/// <summary>
|
|
29
|
-
/// Cria o cliente do alias dado. Chamado <b>uma vez por alias</b> pelo
|
|
30
|
-
/// <see cref="ModelRegistry"/>; o resultado e memoizado.
|
|
31
|
-
/// </summary>
|
|
32
8
|
IChatClient CreateChatClient(ModelAlias alias);
|
|
33
9
|
}
|
|
@@ -3,31 +3,6 @@ using Morph.AiKit.Agents;
|
|
|
3
3
|
|
|
4
4
|
namespace Morph.AiKit.Providers;
|
|
5
5
|
|
|
6
|
-
/// <summary>
|
|
7
|
-
/// Um alias do <c>model-registry.v2.json</c>: o apelido estavel que os agentes citam, com
|
|
8
|
-
/// tudo o que e preciso saber para alcancar o modelo e para contabilizar o que ele custa.
|
|
9
|
-
/// </summary>
|
|
10
|
-
/// <remarks>
|
|
11
|
-
/// <para>
|
|
12
|
-
/// Estende a forma de <c>templates/code/dotnet/ai-agents/ModelRegistry.cs.template</c>
|
|
13
|
-
/// (provider + model + options) com os dois campos que a issue #92 exige e que o template
|
|
14
|
-
/// v1 nao tinha: <see cref="Api"/> — o protocolo, por alias — e a decisao de tarifa
|
|
15
|
-
/// (<see cref="Pricing"/> ou <see cref="PricingWaiver"/>).
|
|
16
|
-
/// </para>
|
|
17
|
-
/// <para>
|
|
18
|
-
/// <b>Invariante do tipo:</b> ou existe <see cref="Pricing"/>, ou existe
|
|
19
|
-
/// <see cref="PricingWaiver"/> — nunca os dois, nunca nenhum. E o que impede
|
|
20
|
-
/// <c>cost_usd</c> de sair nulo la na frente: a ausencia de tarifa passa a ser uma decisao
|
|
21
|
-
/// escrita por alguem, com motivo, e nao um campo que ninguem preencheu.
|
|
22
|
-
/// <see cref="ValidatePricingDecision"/> e o guardiao dessa invariante e e chamado pelo
|
|
23
|
-
/// <see cref="ModelRegistry"/> em toda carga.
|
|
24
|
-
/// </para>
|
|
25
|
-
/// </remarks>
|
|
26
|
-
/// <param name="Provider">Familia de SDK: <c>openai</c>, <c>google</c>, <c>ollama</c>...</param>
|
|
27
|
-
/// <param name="Model">Id do modelo no provider.</param>
|
|
28
|
-
/// <param name="Api">Protocolo deste alias. Ver <see cref="AgentApi"/>.</param>
|
|
29
|
-
/// <param name="Pricing">Tarifa por 1M de tokens, ou nulo se houver <see cref="PricingWaiver"/>.</param>
|
|
30
|
-
/// <param name="Endpoint">Endpoint alternativo (Ollama, gateway). Nulo = o padrao do SDK.</param>
|
|
31
6
|
public sealed record ModelAlias(
|
|
32
7
|
string Provider,
|
|
33
8
|
string Model,
|
|
@@ -35,35 +10,17 @@ public sealed record ModelAlias(
|
|
|
35
10
|
ModelPricing? Pricing,
|
|
36
11
|
string? Endpoint)
|
|
37
12
|
{
|
|
38
|
-
/// <summary>A chave do alias no documento — <c>text-default</c>, <c>reasoning</c>...</summary>
|
|
39
13
|
public required string Name { get; init; }
|
|
40
14
|
|
|
41
|
-
/// <summary>
|
|
42
|
-
/// Motivo, por extenso, de este alias nao ter tarifa. Preenchido <b>somente</b> quando
|
|
43
|
-
/// <see cref="Pricing"/> e nulo. Exemplo real: modelo local, onde a conta e a maquina e
|
|
44
|
-
/// nao o token.
|
|
45
|
-
/// </summary>
|
|
46
15
|
public string? PricingWaiver { get; init; }
|
|
47
16
|
|
|
48
|
-
/// <summary>Limites conhecidos deste alias. Ver <see cref="ModelAliasConstraints"/>.</summary>
|
|
49
17
|
public ModelAliasConstraints Constraints { get; init; } = ModelAliasConstraints.None;
|
|
50
18
|
|
|
51
|
-
/// <summary>Se ha tarifa. Quando falso, <see cref="PricingWaiver"/> explica por que.</summary>
|
|
52
19
|
public bool HasPricing => Pricing is not null;
|
|
53
20
|
|
|
54
|
-
/// <summary>
|
|
55
|
-
/// A tarifa, ou uma falha que diz o nome do alias e o motivo declarado da ausencia.
|
|
56
|
-
/// Use quando o custo e obrigatorio; use <see cref="HasPricing"/> quando ausencia de
|
|
57
|
-
/// tarifa e um caminho previsto (e o caso do <c>UsageCostMiddleware</c>, que registra
|
|
58
|
-
/// tokens com custo zero e sinaliza a ausencia — nunca inventa tarifa).
|
|
59
|
-
/// </summary>
|
|
60
21
|
public ModelPricing RequirePricing()
|
|
61
22
|
=> Pricing ?? throw ModelRegistryException.MissingPricing(Name, PricingWaiver);
|
|
62
23
|
|
|
63
|
-
/// <summary>
|
|
64
|
-
/// Cobra a invariante "tarifa OU dispensa, nunca ambas, nunca nenhuma".
|
|
65
|
-
/// </summary>
|
|
66
|
-
/// <exception cref="ModelRegistryException">Quando a decisao de tarifa nao foi tomada.</exception>
|
|
67
24
|
public static void ValidatePricingDecision(ModelAlias alias)
|
|
68
25
|
{
|
|
69
26
|
ArgumentNullException.ThrowIfNull(alias);
|
|
@@ -87,21 +44,6 @@ public sealed record ModelAlias(
|
|
|
87
44
|
}
|
|
88
45
|
}
|
|
89
46
|
|
|
90
|
-
/// <summary>
|
|
91
|
-
/// Confronta uma requisicao com os limites declarados deste alias, <b>sem lancar</b> —
|
|
92
|
-
/// a decisao de abortar, avisar ou seguir e de quem chama (a <c>AgentFactory</c>).
|
|
93
|
-
/// </summary>
|
|
94
|
-
/// <param name="effectiveApi">
|
|
95
|
-
/// O protocolo que vai valer de fato: <c>spec.Api ?? alias.Api</c>. Veja
|
|
96
|
-
/// <see cref="AgentSpec.ResolveApi"/> — a precedencia mora la, nao aqui.
|
|
97
|
-
/// </param>
|
|
98
|
-
/// <param name="hasTools">Se a requisicao leva tools.</param>
|
|
99
|
-
/// <param name="reasoningEffort">
|
|
100
|
-
/// O esforco que sera enviado, ou nulo quando nada sera enviado. <b>Nulo nao e
|
|
101
|
-
/// violacao</b>: nesse caso vale o default do provider, e qual e ele o kit nao afirma
|
|
102
|
-
/// saber — modelar o contrario seria trocar um fato por folclore.
|
|
103
|
-
/// </param>
|
|
104
|
-
/// <param name="temperatureIsSet">Se a requisicao envia temperatura.</param>
|
|
105
47
|
public AliasRequestCheck CheckRequest(
|
|
106
48
|
AgentApi effectiveApi,
|
|
107
49
|
bool hasTools,
|
|
@@ -135,38 +77,13 @@ public sealed record ModelAlias(
|
|
|
135
77
|
}
|
|
136
78
|
}
|
|
137
79
|
|
|
138
|
-
/// <summary>
|
|
139
|
-
/// Tarifa de um alias, em USD por 1 milhao de tokens. Vive junto do alias porque a conta
|
|
140
|
-
/// so faz sentido com a tarifa daquele modelo — e porque a matematica do custo, escrita
|
|
141
|
-
/// uma vez aqui, nao pode divergir entre o middleware (T11) e um relatorio qualquer.
|
|
142
|
-
/// </summary>
|
|
143
|
-
/// <param name="InputPerMTok">USD por 1M de tokens de entrada.</param>
|
|
144
|
-
/// <param name="OutputPerMTok">USD por 1M de tokens de saida.</param>
|
|
145
|
-
/// <param name="CachedInputPerMTok">
|
|
146
|
-
/// USD por 1M de tokens de entrada servidos do cache do provider. Nulo = <b>nao ha desconto
|
|
147
|
-
/// declarado</b>, e entao token cacheado e cobrado como entrada normal. Nulo NUNCA significa
|
|
148
|
-
/// "de graca": inventar desconto e tao errado quanto inventar tarifa.
|
|
149
|
-
/// </param>
|
|
150
80
|
public sealed record ModelPricing(
|
|
151
81
|
decimal InputPerMTok,
|
|
152
82
|
decimal OutputPerMTok,
|
|
153
83
|
decimal? CachedInputPerMTok)
|
|
154
84
|
{
|
|
155
|
-
/// <summary>Tokens a que a tarifa se refere: 1 milhao.</summary>
|
|
156
85
|
public const decimal TokensPerTariffUnit = 1_000_000m;
|
|
157
86
|
|
|
158
|
-
/// <summary>
|
|
159
|
-
/// Custo em USD de uma volta, a partir das contagens de token da resposta.
|
|
160
|
-
/// </summary>
|
|
161
|
-
/// <remarks>
|
|
162
|
-
/// <paramref name="cachedInputTokens"/> e um <b>subconjunto</b> de
|
|
163
|
-
/// <paramref name="inputTokens"/> — e assim que o provider reporta e assim que
|
|
164
|
-
/// <c>UsageDetails.CachedInputTokenCount</c> espelha. A parte cacheada so e cobrada a
|
|
165
|
-
/// parte quando <see cref="CachedInputPerMTok"/> existe; sem tarifa de cache, toda a
|
|
166
|
-
/// entrada e cobrada pela tarifa de entrada.
|
|
167
|
-
/// </remarks>
|
|
168
|
-
/// <exception cref="ArgumentOutOfRangeException">Contagem negativa.</exception>
|
|
169
|
-
/// <exception cref="ArgumentException">Tokens cacheados maiores que os de entrada.</exception>
|
|
170
87
|
public decimal ComputeCostUsd(long inputTokens, long outputTokens, long? cachedInputTokens = null)
|
|
171
88
|
{
|
|
172
89
|
ArgumentOutOfRangeException.ThrowIfNegative(inputTokens);
|
|
@@ -196,52 +113,18 @@ public sealed record ModelPricing(
|
|
|
196
113
|
}
|
|
197
114
|
}
|
|
198
115
|
|
|
199
|
-
/// <summary>
|
|
200
|
-
/// Limites conhecidos de um alias. <b>Por alias, com procedencia escrita</b> — nunca uma
|
|
201
|
-
/// regra universal chumbada no codigo.
|
|
202
|
-
/// </summary>
|
|
203
|
-
/// <remarks>
|
|
204
|
-
/// A diferenca importa: "modelo de raciocinio rejeita tool" e folclore; o que a
|
|
205
|
-
/// documentacao diz e mais estreito e cabe num campo. Um limite sem
|
|
206
|
-
/// <see cref="Provenance"/> e recusado na carga do registry, justamente para que ninguem
|
|
207
|
-
/// consiga gravar um "todo mundo sabe que" sem dizer de onde tirou.
|
|
208
|
-
/// </remarks>
|
|
209
116
|
public sealed record ModelAliasConstraints
|
|
210
117
|
{
|
|
211
|
-
/// <summary>Nenhum limite declarado.</summary>
|
|
212
118
|
public static readonly ModelAliasConstraints None = new();
|
|
213
119
|
|
|
214
|
-
/// <summary>
|
|
215
|
-
/// Na <b>Chat Completions</b>, este modelo so aceita tool calling com reasoning effort
|
|
216
|
-
/// <c>none</c>. E o limite exato descrito pela documentacao — mais estreito que
|
|
217
|
-
/// "modelo de raciocinio nao aceita tool" — e por isso e verificado com os quatro fatos
|
|
218
|
-
/// juntos em <see cref="ModelAlias.CheckRequest"/>: protocolo Chat, tools presentes,
|
|
219
|
-
/// effort enviado, e effort diferente de <c>none</c>.
|
|
220
|
-
/// </summary>
|
|
221
120
|
public bool ToolCallingRequiresReasoningEffortNone { get; init; }
|
|
222
121
|
|
|
223
|
-
/// <summary>
|
|
224
|
-
/// Este modelo nao aceita <c>temperature</c>.
|
|
225
|
-
/// <b>Existe como flag por alias exatamente porque a afirmacao generica
|
|
226
|
-
/// ("temperatura e obsoleta para modelos de raciocinio") NAO foi confirmada em fonte
|
|
227
|
-
/// primaria.</b> Quem ligar isto assume a afirmacao para um modelo especifico e escreve
|
|
228
|
-
/// de onde tirou em <see cref="Provenance"/>.
|
|
229
|
-
/// </summary>
|
|
230
122
|
public bool TemperatureUnsupported { get; init; }
|
|
231
123
|
|
|
232
|
-
/// <summary>
|
|
233
|
-
/// De onde veio o limite: documento, data, citacao. Obrigatorio quando qualquer flag
|
|
234
|
-
/// esta ligada.
|
|
235
|
-
/// </summary>
|
|
236
124
|
public string? Provenance { get; init; }
|
|
237
125
|
|
|
238
|
-
/// <summary>Se ha qualquer limite declarado.</summary>
|
|
239
126
|
public bool AnyFlagSet => ToolCallingRequiresReasoningEffortNone || TemperatureUnsupported;
|
|
240
127
|
|
|
241
|
-
/// <summary>
|
|
242
|
-
/// Cobra "limite ligado exige procedencia".
|
|
243
|
-
/// </summary>
|
|
244
|
-
/// <exception cref="ModelRegistryException">Flag ligada sem procedencia.</exception>
|
|
245
128
|
public void ValidateProvenance(string aliasName)
|
|
246
129
|
{
|
|
247
130
|
if (AnyFlagSet && string.IsNullOrWhiteSpace(Provenance))
|
|
@@ -254,14 +137,7 @@ public sealed record ModelAliasConstraints
|
|
|
254
137
|
}
|
|
255
138
|
}
|
|
256
139
|
|
|
257
|
-
/// <summary>
|
|
258
|
-
/// Resultado de <see cref="ModelAlias.CheckRequest"/>. Result, nao excecao: um limite
|
|
259
|
-
/// violado e informacao para quem chama decidir, nao fluxo de controle.
|
|
260
|
-
/// </summary>
|
|
261
|
-
/// <param name="IsSatisfied">Se a requisicao respeita todos os limites do alias.</param>
|
|
262
|
-
/// <param name="Violations">Uma frase por limite violado, ja com a procedencia.</param>
|
|
263
140
|
public sealed record AliasRequestCheck(bool IsSatisfied, IReadOnlyList<string> Violations)
|
|
264
141
|
{
|
|
265
|
-
/// <summary>Nenhuma violacao.</summary>
|
|
266
142
|
public static readonly AliasRequestCheck Satisfied = new(true, []);
|
|
267
143
|
}
|
|
@@ -4,28 +4,6 @@ using Morph.AiKit.Agents;
|
|
|
4
4
|
|
|
5
5
|
namespace Morph.AiKit.Providers;
|
|
6
6
|
|
|
7
|
-
/// <summary>
|
|
8
|
-
/// Fonte unica de "que modelo e esse apelido": le o documento
|
|
9
|
-
/// <c>model-registry.v2.json</c>, resolve alias e memoiza um <see cref="IChatClient"/> por
|
|
10
|
-
/// alias.
|
|
11
|
-
/// </summary>
|
|
12
|
-
/// <remarks>
|
|
13
|
-
/// <para>
|
|
14
|
-
/// Estende a forma de <c>templates/code/dotnet/ai-agents/ModelRegistry.cs.template</c> —
|
|
15
|
-
/// documento JSON como fonte unica, cache de cliente por alias — e acrescenta as duas
|
|
16
|
-
/// coisas que a issue #92 cobra: <b><c>api:</c> por alias</b> e <b>tarifa por alias</b>.
|
|
17
|
-
/// A construcao do cliente saiu de dentro da classe e virou
|
|
18
|
-
/// <see cref="IProviderClientFactory"/>: e essa mudanca que torna o kit testavel em CI sem
|
|
19
|
-
/// rede e sem chave de API.
|
|
20
|
-
/// </para>
|
|
21
|
-
/// <para>
|
|
22
|
-
/// <b>Toda falha aqui e falha de carga, nao fluxo de controle.</b> Documento invalido,
|
|
23
|
-
/// alias sem decisao de tarifa, limite sem procedencia: tudo estoura em
|
|
24
|
-
/// <see cref="FromJson"/>, na composicao, com o nome do alias e o motivo — nunca vira um
|
|
25
|
-
/// nulo que atravessa tres camadas e aparece como custo zerado num relatorio. Para o
|
|
26
|
-
/// caminho quente ha <see cref="TryResolve"/>, que nao lanca.
|
|
27
|
-
/// </para>
|
|
28
|
-
/// </remarks>
|
|
29
7
|
public sealed class ModelRegistry : IDisposable
|
|
30
8
|
{
|
|
31
9
|
private const string SupportedDocumentVersion = "2.0";
|
|
@@ -48,6 +26,7 @@ public sealed class ModelRegistry : IDisposable
|
|
|
48
26
|
};
|
|
49
27
|
|
|
50
28
|
private readonly Dictionary<string, ModelAlias> _aliases;
|
|
29
|
+
// Um cliente por alias: cliente de provider carrega pool de conexao, e recria-lo por chamada vaza socket.
|
|
51
30
|
private readonly Dictionary<string, IChatClient> _clients = new(StringComparer.Ordinal);
|
|
52
31
|
private readonly IProviderClientFactory _factory;
|
|
53
32
|
private readonly Lock _gate = new();
|
|
@@ -63,21 +42,10 @@ public sealed class ModelRegistry : IDisposable
|
|
|
63
42
|
DefaultAlias = defaultAlias;
|
|
64
43
|
}
|
|
65
44
|
|
|
66
|
-
/// <summary>Alias usado quando quem chama nao escolhe um. Nulo se o documento nao declara.</summary>
|
|
67
45
|
public string? DefaultAlias { get; }
|
|
68
46
|
|
|
69
|
-
/// <summary>Os aliases do documento, na ordem em que foram declarados.</summary>
|
|
70
47
|
public IReadOnlyCollection<string> Aliases => _aliases.Keys;
|
|
71
48
|
|
|
72
|
-
/// <summary>
|
|
73
|
-
/// Le o documento e valida tudo o que pode ser validado antes de a primeira requisicao
|
|
74
|
-
/// existir.
|
|
75
|
-
/// </summary>
|
|
76
|
-
/// <exception cref="ModelRegistryException">
|
|
77
|
-
/// JSON invalido, campo obrigatorio faltando, <c>api</c> desconhecida, chave nao
|
|
78
|
-
/// reconhecida, tarifa negativa, alias sem decisao de tarifa, ou limite sem procedencia.
|
|
79
|
-
/// A mensagem sempre nomeia o alias e o campo.
|
|
80
|
-
/// </exception>
|
|
81
49
|
public static ModelRegistry FromJson(string json, IProviderClientFactory factory)
|
|
82
50
|
{
|
|
83
51
|
ArgumentException.ThrowIfNullOrWhiteSpace(json);
|
|
@@ -155,12 +123,6 @@ public sealed class ModelRegistry : IDisposable
|
|
|
155
123
|
}
|
|
156
124
|
}
|
|
157
125
|
|
|
158
|
-
/// <summary>
|
|
159
|
-
/// O alias, ou falha nomeando os aliases que existem.
|
|
160
|
-
/// <b>Nunca devolve nulo</b> — alias desconhecido e erro de configuracao, e um nulo
|
|
161
|
-
/// silencioso aqui vira um <c>NullReferenceException</c> em outro arquivo.
|
|
162
|
-
/// </summary>
|
|
163
|
-
/// <exception cref="ModelRegistryException">Alias desconhecido.</exception>
|
|
164
126
|
public ModelAlias Resolve(string alias)
|
|
165
127
|
{
|
|
166
128
|
ArgumentException.ThrowIfNullOrWhiteSpace(alias);
|
|
@@ -170,7 +132,6 @@ public sealed class ModelRegistry : IDisposable
|
|
|
170
132
|
: throw ModelRegistryException.UnknownAlias(alias, _aliases.Keys);
|
|
171
133
|
}
|
|
172
134
|
|
|
173
|
-
/// <summary>Versao sem excecao de <see cref="Resolve"/>, para caminho quente.</summary>
|
|
174
135
|
public bool TryResolve(string alias, out ModelAlias? resolved)
|
|
175
136
|
{
|
|
176
137
|
ArgumentException.ThrowIfNullOrWhiteSpace(alias);
|
|
@@ -178,14 +139,6 @@ public sealed class ModelRegistry : IDisposable
|
|
|
178
139
|
return _aliases.TryGetValue(alias, out resolved);
|
|
179
140
|
}
|
|
180
141
|
|
|
181
|
-
/// <summary>
|
|
182
|
-
/// O <see cref="IChatClient"/> do alias, <b>memoizado</b>: a fabrica e chamada uma unica
|
|
183
|
-
/// vez por alias, e as chamadas seguintes devolvem a mesma instancia. Cliente de provider
|
|
184
|
-
/// carrega pool de conexao e handler; recria-lo por chamada e como o vazamento de socket
|
|
185
|
-
/// costuma nascer.
|
|
186
|
-
/// </summary>
|
|
187
|
-
/// <exception cref="ModelRegistryException">Alias desconhecido, ou fabrica devolvendo nulo.</exception>
|
|
188
|
-
/// <exception cref="ObjectDisposedException">O registry ja foi descartado.</exception>
|
|
189
142
|
public IChatClient GetChatClient(string alias)
|
|
190
143
|
{
|
|
191
144
|
var resolved = Resolve(alias);
|
|
@@ -209,10 +162,6 @@ public sealed class ModelRegistry : IDisposable
|
|
|
209
162
|
}
|
|
210
163
|
}
|
|
211
164
|
|
|
212
|
-
/// <summary>
|
|
213
|
-
/// Descarta os clientes memoizados. O registry e dono do que a fabrica criou — ver a
|
|
214
|
-
/// nota de ownership em <see cref="IProviderClientFactory"/>.
|
|
215
|
-
/// </summary>
|
|
216
165
|
public void Dispose()
|
|
217
166
|
{
|
|
218
167
|
List<IChatClient> toDispose;
|
|
@@ -261,8 +210,6 @@ public sealed class ModelRegistry : IDisposable
|
|
|
261
210
|
Constraints = ReadConstraints(name, element),
|
|
262
211
|
};
|
|
263
212
|
|
|
264
|
-
// As duas invariantes que fazem este registry valer a pena: a decisao de tarifa e
|
|
265
|
-
// explicita, e todo limite declarado diz de onde veio.
|
|
266
213
|
ModelAlias.ValidatePricingDecision(alias);
|
|
267
214
|
alias.Constraints.ValidateProvenance(name);
|
|
268
215
|
|
|
@@ -273,8 +220,6 @@ public sealed class ModelRegistry : IDisposable
|
|
|
273
220
|
{
|
|
274
221
|
if (!element.TryGetProperty("api", out var api))
|
|
275
222
|
{
|
|
276
|
-
// Omitir 'api' e cair na Responses API — a decisao da casa. Chat Completions
|
|
277
|
-
// exige alguem escrever "chat", ou seja, exige assumir a escolha.
|
|
278
223
|
return AgentApi.Responses;
|
|
279
224
|
}
|
|
280
225
|
|
|
@@ -284,9 +229,6 @@ public sealed class ModelRegistry : IDisposable
|
|
|
284
229
|
{
|
|
285
230
|
"responses" => AgentApi.Responses,
|
|
286
231
|
"chat" => AgentApi.Chat,
|
|
287
|
-
// Midia entra no MESMO registry: um vocabulario so de alias, um lugar so onde
|
|
288
|
-
// se pergunta "que modelos este projeto usa". Um alias assim nao vira agente —
|
|
289
|
-
// a AgentFactory o recusa com erro nomeado.
|
|
290
232
|
"images" => AgentApi.Images,
|
|
291
233
|
"videos" => AgentApi.Videos,
|
|
292
234
|
_ => throw ModelRegistryException.InvalidDocument(
|
|
@@ -416,55 +358,41 @@ public sealed class ModelRegistry : IDisposable
|
|
|
416
358
|
}
|
|
417
359
|
}
|
|
418
360
|
|
|
419
|
-
/// <summary>Motivo de uma falha do <see cref="ModelRegistry"/>. Discrimina sem exigir hierarquia de excecao.</summary>
|
|
420
361
|
public enum ModelRegistryError
|
|
421
362
|
{
|
|
422
|
-
/// <summary>Documento invalido: JSON quebrado, campo faltando, valor fora do contrato.</summary>
|
|
423
363
|
InvalidDocument = 0,
|
|
424
364
|
|
|
425
|
-
/// <summary>Alias inexistente no documento carregado.</summary>
|
|
426
365
|
UnknownAlias = 1,
|
|
427
366
|
|
|
428
|
-
/// <summary>Alias sem tarifa, com o motivo da ausencia declarado no <c>pricingWaiver</c>.</summary>
|
|
429
367
|
MissingPricing = 2,
|
|
430
368
|
}
|
|
431
369
|
|
|
432
|
-
/// <summary>
|
|
433
|
-
/// Falha de configuracao do registry. <c>sealed</c> com um <see cref="Reason"/> em vez de
|
|
434
|
-
/// uma arvore de subclasses: quem trata precisa discriminar o motivo, nao herdar dele.
|
|
435
|
-
/// </summary>
|
|
436
370
|
public sealed class ModelRegistryException : InvalidOperationException
|
|
437
371
|
{
|
|
438
372
|
private ModelRegistryException(ModelRegistryError reason, string message, Exception? inner = null)
|
|
439
373
|
: base(message, inner)
|
|
440
374
|
=> Reason = reason;
|
|
441
375
|
|
|
442
|
-
/// <summary>Cria a excecao sem motivo especifico. Prefira as fabricas nomeadas.</summary>
|
|
443
376
|
public ModelRegistryException(string message)
|
|
444
377
|
: this(ModelRegistryError.InvalidDocument, message)
|
|
445
378
|
{
|
|
446
379
|
}
|
|
447
380
|
|
|
448
|
-
/// <summary>Cria a excecao encadeando a causa.</summary>
|
|
449
381
|
public ModelRegistryException(string message, Exception innerException)
|
|
450
382
|
: this(ModelRegistryError.InvalidDocument, message, innerException)
|
|
451
383
|
{
|
|
452
384
|
}
|
|
453
385
|
|
|
454
|
-
/// <summary>Por que falhou.</summary>
|
|
455
386
|
public ModelRegistryError Reason { get; }
|
|
456
387
|
|
|
457
|
-
/// <summary>Documento fora do contrato.</summary>
|
|
458
388
|
public static ModelRegistryException InvalidDocument(string message, Exception? inner = null)
|
|
459
389
|
=> new(ModelRegistryError.InvalidDocument, message, inner);
|
|
460
390
|
|
|
461
|
-
/// <summary>Alias desconhecido — a mensagem lista os que existem.</summary>
|
|
462
391
|
public static ModelRegistryException UnknownAlias(string alias, IEnumerable<string> known)
|
|
463
392
|
=> new(
|
|
464
393
|
ModelRegistryError.UnknownAlias,
|
|
465
394
|
$"alias desconhecido: '{alias}'. Declarados no model-registry: {string.Join(", ", known)}.");
|
|
466
395
|
|
|
467
|
-
/// <summary>Tarifa exigida de um alias que declarou dispensa.</summary>
|
|
468
396
|
public static ModelRegistryException MissingPricing(string alias, string? waiver)
|
|
469
397
|
=> new(
|
|
470
398
|
ModelRegistryError.MissingPricing,
|
|
@@ -1,22 +1,5 @@
|
|
|
1
1
|
namespace Morph.AiKit.Rag;
|
|
2
2
|
|
|
3
|
-
/// <summary>
|
|
4
|
-
/// Erro de <b>entrada</b> do <see cref="DeltaIngest"/>: a mesma lookup-key apareceu duas
|
|
5
|
-
/// vezes na mesma remessa.
|
|
6
|
-
/// </summary>
|
|
7
|
-
/// <remarks>
|
|
8
|
-
/// <para>
|
|
9
|
-
/// Nao e fluxo de controle e nao e recuperavel no lugar: com a chave repetida, "o que esta
|
|
10
|
-
/// no acervo" deixa de ter resposta unica e o plano vira nao-deterministico — qual dos dois
|
|
11
|
-
/// itens venceu passa a depender da ordem de enumeracao. Silenciar (ficar com o ultimo)
|
|
12
|
-
/// gravaria um dos dois ao acaso; deduplicar por conta propria esconderia um defeito da
|
|
13
|
-
/// origem, que e onde ele tem conserto.
|
|
14
|
-
/// </para>
|
|
15
|
-
/// <para>
|
|
16
|
-
/// Os dois indices vao na excecao porque "chave X duplicada" manda procurar num corpus de
|
|
17
|
-
/// milhares; "posicoes 12 e 4.871" manda olhar duas linhas.
|
|
18
|
-
/// </para>
|
|
19
|
-
/// </remarks>
|
|
20
3
|
public sealed class DuplicateLookupKeyException : ArgumentException
|
|
21
4
|
{
|
|
22
5
|
internal DuplicateLookupKeyException(string lookupKey, int firstIndex, int duplicateIndex, string paramName)
|
|
@@ -32,58 +15,16 @@ public sealed class DuplicateLookupKeyException : ArgumentException
|
|
|
32
15
|
DuplicateIndex = duplicateIndex;
|
|
33
16
|
}
|
|
34
17
|
|
|
35
|
-
/// <summary>A chave repetida.</summary>
|
|
36
18
|
public string LookupKey { get; }
|
|
37
19
|
|
|
38
|
-
/// <summary>Onde ela apareceu pela primeira vez.</summary>
|
|
39
20
|
public int FirstIndex { get; }
|
|
40
21
|
|
|
41
|
-
/// <summary>Onde ela reapareceu.</summary>
|
|
42
22
|
public int DuplicateIndex { get; }
|
|
43
23
|
}
|
|
44
24
|
|
|
45
|
-
/// <summary>
|
|
46
|
-
/// Planejamento de ingestao <b>idempotente por lookup-key</b>: dada uma remessa e o que o
|
|
47
|
-
/// acervo ja tem, diz o que entrou, o que mudou, o que sumiu e o que ficou igual.
|
|
48
|
-
/// </summary>
|
|
49
|
-
/// <remarks>
|
|
50
|
-
/// <para>
|
|
51
|
-
/// <b>Funcao pura, sem I/O.</b> Nao abre conexao, nao chama embedding, nao mede tempo — o
|
|
52
|
-
/// que a torna testavel sem banco e, mais importante, torna a idempotencia <b>demonstravel</b>
|
|
53
|
-
/// em vez de prometida: aplicar o plano e replanejar a mesma entrada tem de devolver
|
|
54
|
-
/// <see cref="DeltaPlan{T}.IsNoOp"/> verdadeiro.
|
|
55
|
-
/// </para>
|
|
56
|
-
/// <para>
|
|
57
|
-
/// <b>A chave e a identidade; o hash e a versao.</b> Sao duas perguntas diferentes e por isso
|
|
58
|
-
/// dois delegados: <c>lookupKey</c> responde "e o mesmo documento?" e <c>contentHash</c>
|
|
59
|
-
/// responde "mudou?". Colapsar as duas num campo so — usar o hash como chave — faz cada
|
|
60
|
-
/// edicao parecer um documento novo e o original parecer removido, que e a ingestao
|
|
61
|
-
/// apaga-tudo com outro nome.
|
|
62
|
-
/// </para>
|
|
63
|
-
/// </remarks>
|
|
64
25
|
public static class DeltaIngest
|
|
65
26
|
{
|
|
66
|
-
|
|
67
|
-
/// Compara a remessa com o acervo e devolve o plano.
|
|
68
|
-
/// </summary>
|
|
69
|
-
/// <typeparam name="T">O item ingerido.</typeparam>
|
|
70
|
-
/// <param name="incoming">A remessa. Enumerada uma unica vez.</param>
|
|
71
|
-
/// <param name="lookupKey">
|
|
72
|
-
/// A identidade estavel do item — o que faz duas versoes do mesmo documento serem o
|
|
73
|
-
/// mesmo documento. Nula ou em branco e recusada: uma chave vazia colapsaria itens
|
|
74
|
-
/// distintos num so.
|
|
75
|
-
/// </param>
|
|
76
|
-
/// <param name="contentHash">
|
|
77
|
-
/// A versao do conteudo. Igual significa "nao mudou"; diferente significa "mudou". Nulo e
|
|
78
|
-
/// recusado — sem hash nao ha como distinguir <c>Unchanged</c> de <c>Updated</c>, e
|
|
79
|
-
/// tratar a ausencia como "mudou" reintroduziria o re-embedding de tudo.
|
|
80
|
-
/// </param>
|
|
81
|
-
/// <param name="existingHashes">
|
|
82
|
-
/// O que o acervo tem hoje: lookup-key para hash de conteudo. As chaves daqui que nao
|
|
83
|
-
/// aparecerem na remessa saem em <see cref="DeltaPlan{T}.Removed"/>.
|
|
84
|
-
/// </param>
|
|
85
|
-
/// <exception cref="DuplicateLookupKeyException">A mesma chave veio duas vezes.</exception>
|
|
86
|
-
/// <exception cref="ArgumentException">Um item produziu chave ou hash nulo/em branco.</exception>
|
|
27
|
+
// A chave e a identidade e o hash e a versao: usar o hash como chave faz cada edicao virar remocao + insercao.
|
|
87
28
|
public static DeltaPlan<T> Plan<T>(
|
|
88
29
|
IEnumerable<T> incoming,
|
|
89
30
|
Func<T, string> lookupKey,
|
|
@@ -99,9 +40,6 @@ public static class DeltaIngest
|
|
|
99
40
|
List<T> updated = [];
|
|
100
41
|
List<T> unchanged = [];
|
|
101
42
|
|
|
102
|
-
// Comparacao ordinal, explicita: a chave e um identificador, nao texto de usuario.
|
|
103
|
-
// Deixar a cultura decidir faria o mesmo corpus produzir planos diferentes conforme a
|
|
104
|
-
// maquina que roda a ingestao.
|
|
105
43
|
var seen = new Dictionary<string, int>(StringComparer.Ordinal);
|
|
106
44
|
var index = 0;
|
|
107
45
|
|
|
@@ -151,9 +89,6 @@ public static class DeltaIngest
|
|
|
151
89
|
index++;
|
|
152
90
|
}
|
|
153
91
|
|
|
154
|
-
// O que o acervo tem e a remessa nao trouxe. Note que 'seen' e consultado pela MESMA
|
|
155
|
-
// chave e com o MESMO comparador usados acima — uma segunda nocao de igualdade aqui
|
|
156
|
-
// faria itens presentes serem reportados como removidos.
|
|
157
92
|
List<string> removed = [];
|
|
158
93
|
|
|
159
94
|
foreach (var key in existingHashes.Keys)
|