@polymorphism-tech/morph-spec 7.0.0 → 7.1.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.
Files changed (89) hide show
  1. package/README.md +368 -368
  2. package/claude-plugin.json +14 -14
  3. package/docs/CHEATSHEET.md +199 -199
  4. package/docs/QUICKSTART.md +200 -200
  5. package/framework/CLAUDE.md +1 -1
  6. package/framework/MORPH.md +4 -1
  7. package/framework/agents.json +132 -127
  8. package/framework/rubrics/backend.json +41 -36
  9. package/framework/rules/ai-agents-standards.md +45 -15
  10. package/framework/rules/neon-standards.md +25 -26
  11. package/framework/skills/core/morph-brainstorming/SKILL.md +1 -1
  12. package/framework/skills/core/morph-plan/SKILL.md +3 -1
  13. package/framework/skills/core/morph-stack-scan/SKILL.md +3 -3
  14. package/framework/standards/STANDARDS.json +1729 -1133
  15. package/framework/standards/ai-agents/agent-session.md +160 -0
  16. package/framework/standards/ai-agents/batching.md +193 -0
  17. package/framework/standards/ai-agents/context-providers.md +188 -0
  18. package/framework/standards/ai-agents/mcp-server.md +250 -0
  19. package/framework/standards/ai-agents/mcp-tools.md +197 -0
  20. package/framework/standards/ai-agents/middleware-patterns.md +207 -0
  21. package/framework/standards/ai-agents/modalities-image-gen.md +173 -0
  22. package/framework/standards/ai-agents/modalities-speech-to-text.md +171 -0
  23. package/framework/standards/ai-agents/modalities-text-to-speech.md +181 -0
  24. package/framework/standards/ai-agents/modalities-vision.md +201 -0
  25. package/framework/standards/ai-agents/multi-agent-patterns.md +155 -0
  26. package/framework/standards/ai-agents/production.md +114 -258
  27. package/framework/standards/ai-agents/prompts-in-db.md +214 -0
  28. package/framework/standards/ai-agents/providers/model-registry.md +158 -0
  29. package/framework/standards/ai-agents/rag-custom-pgvector.md +243 -0
  30. package/framework/standards/ai-agents/service-tier-flex.md +135 -0
  31. package/framework/standards/ai-agents/setup.md +142 -316
  32. package/framework/standards/ai-agents/structured-output.md +249 -0
  33. package/framework/standards/ai-agents/sweet-spot.md +109 -0
  34. package/framework/standards/ai-agents/workflows.md +114 -284
  35. package/framework/standards/architecture/vertical-slice/vertical-slice.md +439 -429
  36. package/framework/standards/backend/api/validation.md +92 -88
  37. package/framework/standards/backend/database/postgresql/database.md +336 -332
  38. package/framework/standards/backend/database/vector-search-rag.md +599 -541
  39. package/framework/standards/backend/dotnet/fluent-validation-vsa.md +4 -0
  40. package/framework/standards/backend/dotnet/result-pattern.md +4 -0
  41. package/framework/standards/backend/dotnet/scalar-ui.md +176 -172
  42. package/framework/standards/backend/dotnet/scrutor.md +4 -0
  43. package/framework/standards/backend/dotnet/vsa-handler-patterns.md +310 -274
  44. package/framework/standards/backend/integrations/asaas/asaas-api.md +260 -216
  45. package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +354 -350
  46. package/framework/standards/backend/integrations/neon-auth/neon-auth.md +30 -19
  47. package/framework/standards/backend/integrations/resend/resend-email.md +400 -385
  48. package/framework/standards/data/nosql/cache/redis.md +106 -97
  49. package/framework/standards/data/nosql/cosmos-db.md +127 -118
  50. package/framework/standards/data/vector-search/rag-chunking.md +113 -104
  51. package/framework/standards/frontend/design-system/ai-image-generation.md +4 -0
  52. package/framework/standards/frontend/design-system/animations.md +164 -160
  53. package/framework/standards/frontend/design-system/naming.md +68 -64
  54. package/framework/standards/frontend/gsap/gsap-core.md +14 -8
  55. package/framework/standards/frontend/gsap/gsap-plugins.md +26 -21
  56. package/framework/standards/frontend/gsap/gsap-scrolltrigger.md +9 -7
  57. package/framework/standards/frontend/nextjs/app-router.md +127 -123
  58. package/framework/standards/frontend/nextjs/components.md +136 -132
  59. package/framework/standards/frontend/nextjs/data-fetching.md +130 -126
  60. package/framework/standards/frontend/nextjs/forms.md +134 -128
  61. package/framework/standards/frontend/nextjs/motion-patterns.md +328 -322
  62. package/framework/standards/frontend/nextjs/naming-conventions.md +71 -67
  63. package/framework/standards/frontend/nextjs/nextjs-patterns.md +229 -225
  64. package/framework/standards/frontend/nextjs/project-structure.md +106 -102
  65. package/framework/standards/frontend/nextjs/state-management.md +76 -72
  66. package/framework/standards/frontend/nextjs/testing.md +115 -111
  67. package/framework/standards/infrastructure/docker/easypanel-deploy.md +196 -192
  68. package/framework/standards/infrastructure/neon/neon-pgrag.md +6 -2
  69. package/framework/standards/infrastructure/neon/neon-pgvector.md +4 -0
  70. package/framework/standards/infrastructure/neon/neon-setup.md +5 -1
  71. package/framework/standards/integration/mcp/mcp-tools.md +471 -462
  72. package/framework/templates/CLAUDE.md.core.template +1 -1
  73. package/framework/templates/REGISTRY.json +1745 -1239
  74. package/framework/templates/code/dotnet/ai-agents/AgentBootstrap.cs.template +39 -0
  75. package/framework/templates/code/dotnet/ai-agents/EmbeddingService.cs.template +68 -0
  76. package/framework/templates/code/dotnet/ai-agents/ModelRegistry.cs.template +117 -0
  77. package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +95 -0
  78. package/framework/templates/code/dotnet/ai-agents/ResilientToolMiddleware.cs.template +61 -0
  79. package/framework/templates/code/dotnet/ai-agents/VectorSearchTool.cs.template +107 -0
  80. package/framework/templates/code/dotnet/ai-agents/model-registry.json.template +58 -0
  81. package/framework/templates/code/dotnet/backend/repository.cs +3 -10
  82. package/framework/templates/code/sql/ai-agents/agent-prompts-migration.sql.template +58 -0
  83. package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -0
  84. package/package.json +89 -83
  85. package/src/core/templates/template-registry.js +289 -256
  86. package/src/lib/generators/context-generator.js +516 -518
  87. package/src/lib/generators/scaffold-generator.js +23 -26
  88. package/framework/standards/infrastructure/neon/neon-semantic-kernel.md +0 -310
  89. package/framework/templates/integrations/semantic-kernel-neon.cs +0 -125
@@ -9,14 +9,8 @@
9
9
 
10
10
  import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync } from 'fs';
11
11
  import { join, dirname, basename } from 'path';
12
- import { fileURLToPath } from 'url';
13
12
  import { renderTemplate } from '../../core/templates/template-renderer.js';
14
-
15
- const __filename = fileURLToPath(import.meta.url);
16
- const __dirname = dirname(__filename);
17
-
18
- /** Framework templates root (relative to this file) */
19
- const FRAMEWORK_TEMPLATES_DIR = join(__dirname, '../../../framework/templates');
13
+ import { resolveFrameworkTemplatePath } from '../../core/templates/template-registry.js';
20
14
 
21
15
  /**
22
16
  * Convert kebab-case to PascalCase.
@@ -67,19 +61,23 @@ export function normalizeNamespaceToKebab(ns) {
67
61
  // .NET VSA Scaffold
68
62
  // ─────────────────────────────────────────────────────────────────────────────
69
63
 
70
- /** Template IDs for .NET slice-level files */
64
+ // Each entry pairs a REGISTRY.json template `id` with the generator-specific
65
+ // output shaping (class-name suffix, target directory). REGISTRY owns the
66
+ // template path; the generator owns where the rendered file lands.
67
+
68
+ /** .NET slice-level files (endpoint/handler/request/response/validator). */
71
69
  const DOTNET_SLICE_TEMPLATES = [
72
- { id: 'endpoint', file: 'endpoint.cs.hbs', suffix: 'Endpoint' },
73
- { id: 'handler', file: 'handler.cs.hbs', suffix: 'Handler' },
74
- { id: 'request', file: 'request.cs.hbs', suffix: 'Request' },
75
- { id: 'response', file: 'response.cs.hbs', suffix: 'Response' },
76
- { id: 'validator', file: 'validator.cs.hbs', suffix: 'Validator' },
70
+ { templateId: 'dotnet-feature-endpoint', suffix: 'Endpoint' },
71
+ { templateId: 'dotnet-feature-handler', suffix: 'Handler' },
72
+ { templateId: 'dotnet-feature-request', suffix: 'Request' },
73
+ { templateId: 'dotnet-feature-response', suffix: 'Response' },
74
+ { templateId: 'dotnet-feature-validator', suffix: 'Validator' },
77
75
  ];
78
76
 
79
- /** Template IDs for .NET feature-level files */
77
+ /** .NET feature-level files (entity, errors). */
80
78
  const DOTNET_FEATURE_TEMPLATES = [
81
- { id: 'entity', file: 'entity.cs.hbs', suffix: '', dir: 'entities' },
82
- { id: 'errors', file: 'errors.cs.hbs', suffix: 'Errors', dir: 'feature' },
79
+ { templateId: 'dotnet-feature-entity', suffix: '', dir: 'entities' },
80
+ { templateId: 'dotnet-feature-errors', suffix: 'Errors', dir: 'feature' },
83
81
  ];
84
82
 
85
83
  /**
@@ -98,7 +96,6 @@ export function scaffoldDotnetSlice(options) {
98
96
 
99
97
  const pascalFeature = toPascalCase(feature);
100
98
  const pascalSlice = toPascalCase(sliceName);
101
- const templateDir = join(FRAMEWORK_TEMPLATES_DIR, 'code/dotnet/feature');
102
99
 
103
100
  const variables = {
104
101
  PROJECT_NAME: normalizeNamespaceToKebab(namespace),
@@ -112,7 +109,7 @@ export function scaffoldDotnetSlice(options) {
112
109
 
113
110
  // 1. Render slice-level templates
114
111
  for (const tmpl of DOTNET_SLICE_TEMPLATES) {
115
- const templatePath = join(templateDir, tmpl.file);
112
+ const templatePath = resolveFrameworkTemplatePath(tmpl.templateId);
116
113
  const outputPath = join(
117
114
  outputDir,
118
115
  'Features',
@@ -133,7 +130,7 @@ export function scaffoldDotnetSlice(options) {
133
130
 
134
131
  // 2. Render feature-level templates (skip if already exist)
135
132
  for (const tmpl of DOTNET_FEATURE_TEMPLATES) {
136
- const templatePath = join(templateDir, tmpl.file);
133
+ const templatePath = resolveFrameworkTemplatePath(tmpl.templateId);
137
134
  let outputPath;
138
135
 
139
136
  if (tmpl.dir === 'entities') {
@@ -164,22 +161,23 @@ export function scaffoldDotnetSlice(options) {
164
161
  // Next.js Scaffold
165
162
  // ─────────────────────────────────────────────────────────────────────────────
166
163
 
167
- /** Template mappings for Next.js feature */
164
+ // Each entry pairs a REGISTRY.json template `id` with where the rendered file
165
+ // lands. REGISTRY owns the template path; the generator owns the output path.
168
166
  const NEXTJS_TEMPLATES = [
169
167
  {
170
- file: 'page.tsx.hbs',
168
+ templateId: 'nextjs-page',
171
169
  outputFn: (kebab) => join('app', '(dashboard)', kebab, 'page.tsx'),
172
170
  },
173
171
  {
174
- file: 'client-component.tsx.hbs',
172
+ templateId: 'nextjs-client-component',
175
173
  outputFn: (kebab) => join('features', kebab, 'components', `${kebab}-list.tsx`),
176
174
  },
177
175
  {
178
- file: 'feature-form.tsx.hbs',
176
+ templateId: 'nextjs-feature-form',
179
177
  outputFn: (kebab) => join('features', kebab, 'components', `create-${kebab}-form.tsx`),
180
178
  },
181
179
  {
182
- file: 'use-feature.ts.hbs',
180
+ templateId: 'nextjs-use-feature',
183
181
  outputFn: (kebab) => join('features', kebab, 'hooks', `use-${kebab}s.ts`),
184
182
  },
185
183
  ];
@@ -197,7 +195,6 @@ export function scaffoldNextjsFeature(options) {
197
195
  const { featureName, outputDir = process.cwd(), dryRun = false } = options;
198
196
 
199
197
  const kebab = featureName.toLowerCase();
200
- const templateDir = join(FRAMEWORK_TEMPLATES_DIR, 'frontend/nextjs');
201
198
 
202
199
  // Next.js templates use camelCase variable name `featureName`
203
200
  const variables = {
@@ -207,7 +204,7 @@ export function scaffoldNextjsFeature(options) {
207
204
  const files = [];
208
205
 
209
206
  for (const tmpl of NEXTJS_TEMPLATES) {
210
- const templatePath = join(templateDir, tmpl.file);
207
+ const templatePath = resolveFrameworkTemplatePath(tmpl.templateId);
211
208
  const relativePath = tmpl.outputFn(kebab);
212
209
  const outputPath = join(outputDir, relativePath);
213
210
 
@@ -1,310 +0,0 @@
1
- # Neon Semantic Kernel Vector Store Standard
2
-
3
- > **Scope:** neon, dotnet-vsa
4
- > **Layer:** 2 (on keyword)
5
- > **Keywords:** semantic kernel, vector store, PostgresVectorStore, VectorStoreTextSearch, MAF
6
- > **Load When:** semantic kernel postgres vector keywords detected
7
-
8
- Stack: .NET VSA + Neon Serverless Postgres + Semantic Kernel
9
-
10
- ## Core Rules
11
-
12
- - ALWAYS call `UseVector()` on `NpgsqlDataSourceBuilder` -- without it the vector store fails silently
13
- - ALWAYS use `AddPostgresVectorStore()` for DI registration -- handles datasource lifecycle
14
- - NEVER mix pgvector-dotnet `Vector` type with SK `ReadOnlyMemory<float>` -- SK uses its own mapping
15
- - ALWAYS annotate data model properties with `[VectorStoreKey]`, `[VectorStoreData]`, `[VectorStoreVector]`
16
- - Use `PostgresCollection<TKey, TRecord>` for typed collection access
17
-
18
- ## NuGet Packages
19
-
20
- ```bash
21
- dotnet add package Microsoft.SemanticKernel.Connectors.PgVector --prerelease
22
- dotnet add package Microsoft.SemanticKernel.Connectors.OpenAI
23
- dotnet add package Npgsql
24
- ```
25
-
26
- ## PostgresVectorStore Initialization
27
-
28
- ### Via Dependency Injection (Recommended)
29
-
30
- ```csharp
31
- using Microsoft.Extensions.DependencyInjection;
32
- using Microsoft.SemanticKernel;
33
- using Npgsql;
34
-
35
- var builder = WebApplication.CreateBuilder(args);
36
-
37
- // Option 1: Connection string (UseVector applied automatically)
38
- builder.Services.AddPostgresVectorStore(
39
- builder.Configuration.GetConnectionString("DefaultConnection")!);
40
-
41
- // Option 2: Manual NpgsqlDataSource (full control)
42
- builder.Services.AddSingleton<NpgsqlDataSource>(sp =>
43
- {
44
- var dataSourceBuilder = new NpgsqlDataSourceBuilder(
45
- builder.Configuration.GetConnectionString("DefaultConnection"));
46
- dataSourceBuilder.UseVector();
47
- return dataSourceBuilder.Build();
48
- });
49
- builder.Services.AddPostgresVectorStore();
50
- ```
51
-
52
- ### Direct Construction
53
-
54
- ```csharp
55
- using Microsoft.SemanticKernel.Connectors.PgVector;
56
- using Npgsql;
57
-
58
- var dataSourceBuilder = new NpgsqlDataSourceBuilder(connectionString);
59
- dataSourceBuilder.UseVector();
60
- var dataSource = dataSourceBuilder.Build();
61
-
62
- var vectorStore = new PostgresVectorStore(dataSource, ownsDataSource: true);
63
- ```
64
-
65
- ## Data Model Definition
66
-
67
- Annotate your record type with SK vector store attributes:
68
-
69
- ```csharp
70
- using System;
71
- using Microsoft.Extensions.VectorData;
72
-
73
- public class KnowledgeEntry
74
- {
75
- [VectorStoreKey(StorageName = "id")]
76
- public Guid Id { get; set; }
77
-
78
- [VectorStoreData(StorageName = "title")]
79
- public string Title { get; set; } = string.Empty;
80
-
81
- [VectorStoreData(StorageName = "content")]
82
- public string Content { get; set; } = string.Empty;
83
-
84
- [VectorStoreData(StorageName = "source")]
85
- public string? Source { get; set; }
86
-
87
- [VectorStoreData(StorageName = "created_at")]
88
- public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow;
89
-
90
- [VectorStoreVector(
91
- Dimensions: 1536,
92
- DistanceFunction = DistanceFunction.CosineDistance,
93
- IndexKind = IndexKind.Hnsw,
94
- StorageName = "embedding")]
95
- public ReadOnlyMemory<float>? Embedding { get; set; }
96
- }
97
- ```
98
-
99
- This maps to the following Postgres table (auto-created by `EnsureCollectionExistsAsync`):
100
-
101
- ```sql
102
- CREATE TABLE IF NOT EXISTS public."knowledge_entries" (
103
- "id" UUID PRIMARY KEY NOT NULL,
104
- "title" TEXT,
105
- "content" TEXT,
106
- "source" TEXT,
107
- "created_at" TIMESTAMPTZ,
108
- "embedding" VECTOR(1536)
109
- );
110
- ```
111
-
112
- ## Collection Operations
113
-
114
- ```csharp
115
- using Microsoft.SemanticKernel.Connectors.PgVector;
116
-
117
- // Get a typed collection
118
- var collection = vectorStore.GetCollection<Guid, KnowledgeEntry>("knowledge_entries");
119
-
120
- // Create table if it doesn't exist (includes HNSW index)
121
- await collection.EnsureCollectionExistsAsync();
122
-
123
- // Upsert a record
124
- var entry = new KnowledgeEntry
125
- {
126
- Id = Guid.NewGuid(),
127
- Title = "Neon Overview",
128
- Content = "Neon is a serverless Postgres platform...",
129
- Source = "docs",
130
- Embedding = new ReadOnlyMemory<float>(embeddingVector)
131
- };
132
- await collection.UpsertAsync(entry);
133
-
134
- // Get by key
135
- var retrieved = await collection.GetAsync(entry.Id);
136
-
137
- // Delete
138
- await collection.DeleteAsync(entry.Id);
139
- ```
140
-
141
- ## Embedding Generation with OpenAI
142
-
143
- ```csharp
144
- using Microsoft.SemanticKernel;
145
- using Microsoft.SemanticKernel.Connectors.OpenAI;
146
- using Microsoft.SemanticKernel.Embeddings;
147
-
148
- // Register embedding service
149
- builder.Services.AddOpenAITextEmbeddingGeneration(
150
- modelId: "text-embedding-3-small",
151
- apiKey: builder.Configuration["OpenAI:ApiKey"]!);
152
-
153
- // Generate embeddings
154
- public class EmbeddingService(ITextEmbeddingGenerationService embeddingService)
155
- {
156
- public async Task<ReadOnlyMemory<float>> GenerateAsync(string text)
157
- {
158
- var embeddings = await embeddingService.GenerateEmbeddingsAsync([text]);
159
- return embeddings[0];
160
- }
161
-
162
- public async Task<IList<ReadOnlyMemory<float>>> GenerateBatchAsync(IList<string> texts)
163
- {
164
- return await embeddingService.GenerateEmbeddingsAsync(texts);
165
- }
166
- }
167
- ```
168
-
169
- ## VectorStoreTextSearch Usage
170
-
171
- `VectorStoreTextSearch<T>` provides a high-level search API that automatically generates embeddings for queries:
172
-
173
- ```csharp
174
- using Microsoft.Extensions.VectorData;
175
- using Microsoft.SemanticKernel.Data;
176
-
177
- // Create text search from collection + embedding service
178
- var textSearch = new VectorStoreTextSearch<KnowledgeEntry>(
179
- collection,
180
- embeddingGenerationService);
181
-
182
- // Search with natural language
183
- var query = "How does Neon branching work?";
184
- KernelSearchResults<TextSearchResult> results = await textSearch.GetTextSearchResultsAsync(
185
- query, new() { Top = 5, Skip = 0 });
186
-
187
- await foreach (TextSearchResult result in results.Results)
188
- {
189
- Console.WriteLine($"Name: {result.Name}");
190
- Console.WriteLine($"Value: {result.Value}");
191
- Console.WriteLine($"Link: {result.Link}");
192
- }
193
- ```
194
-
195
- ### Use as Kernel Plugin (RAG in Prompts)
196
-
197
- ```csharp
198
- using Microsoft.SemanticKernel;
199
- using Microsoft.SemanticKernel.Data;
200
- using Microsoft.SemanticKernel.PromptTemplates.Handlebars;
201
-
202
- var kernel = builder.Build();
203
-
204
- // Register text search as a plugin
205
- var searchPlugin = textSearch.CreateWithGetTextSearchResults("KnowledgeSearch");
206
- kernel.Plugins.Add(searchPlugin);
207
-
208
- // Use in prompt with automatic retrieval
209
- var promptTemplate = """
210
- {{#with (KnowledgeSearch-GetTextSearchResults query)}}
211
- {{#each this}}
212
- Name: {{Name}}
213
- Value: {{Value}}
214
- ---
215
- {{/each}}
216
- {{/with}}
217
-
218
- Based on the above context, answer: {{query}}
219
- """;
220
-
221
- var function = KernelFunctionFactory.CreateFromPrompt(
222
- new PromptTemplateConfig
223
- {
224
- Template = promptTemplate,
225
- TemplateFormat = "handlebars"
226
- },
227
- new HandlebarsPromptTemplateFactory());
228
-
229
- var result = await kernel.InvokeAsync(function,
230
- new KernelArguments { ["query"] = "How does Neon branching work?" });
231
- ```
232
-
233
- ## Integration with MAF Agents
234
-
235
- Use the vector store as a plugin for ChatCompletionAgent to give agents vector memory:
236
-
237
- ```csharp
238
- using Microsoft.SemanticKernel;
239
- using Microsoft.SemanticKernel.Agents;
240
- using Microsoft.SemanticKernel.Data;
241
-
242
- // Build kernel with vector search plugin
243
- var kernelBuilder = Kernel.CreateBuilder();
244
- kernelBuilder.AddOpenAIChatCompletion("gpt-4o", apiKey);
245
- kernelBuilder.AddOpenAITextEmbeddingGeneration("text-embedding-3-small", apiKey);
246
- var kernel = kernelBuilder.Build();
247
-
248
- // Register vector search as a kernel plugin
249
- var searchPlugin = textSearch.CreateWithGetTextSearchResults("KnowledgeBase");
250
- kernel.Plugins.Add(searchPlugin);
251
-
252
- // Create agent with vector memory
253
- var agent = new ChatCompletionAgent
254
- {
255
- Name = "KnowledgeAgent",
256
- Instructions = """
257
- You are a knowledge assistant. Use the KnowledgeBase plugin to search for
258
- relevant information before answering questions. Always cite your sources.
259
- """,
260
- Kernel = kernel,
261
- Arguments = new KernelArguments(
262
- new OpenAIPromptExecutionSettings { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() })
263
- };
264
-
265
- // Agent automatically calls vector search when needed
266
- await foreach (var message in agent.InvokeAsync(chatHistory))
267
- {
268
- Console.WriteLine(message.Content);
269
- }
270
- ```
271
-
272
- ## Service Registration Summary
273
-
274
- ```csharp
275
- // Program.cs -- complete registration
276
- var builder = WebApplication.CreateBuilder(args);
277
-
278
- // 1. Vector store (Neon + pgvector)
279
- builder.Services.AddPostgresVectorStore(
280
- builder.Configuration.GetConnectionString("DefaultConnection")!);
281
-
282
- // 2. Embedding generation
283
- builder.Services.AddOpenAITextEmbeddingGeneration(
284
- modelId: "text-embedding-3-small",
285
- apiKey: builder.Configuration["OpenAI:ApiKey"]!);
286
-
287
- // 3. Chat completion (for agents)
288
- builder.Services.AddOpenAIChatCompletion(
289
- modelId: "gpt-4o",
290
- apiKey: builder.Configuration["OpenAI:ApiKey"]!);
291
-
292
- // 4. Application services
293
- builder.Services.AddSingleton<EmbeddingService>();
294
- builder.Services.AddSingleton(sp =>
295
- {
296
- var vectorStore = sp.GetRequiredService<PostgresVectorStore>();
297
- return vectorStore.GetCollection<Guid, KnowledgeEntry>("knowledge_entries");
298
- });
299
- ```
300
-
301
- ## Common Mistakes
302
-
303
- | Wrong | Right | Why |
304
- |-------|-------|-----|
305
- | Missing `UseVector()` on data source | Always call `dataSourceBuilder.UseVector()` | Vector type mapping fails without it |
306
- | Using `float[]` for vector property | `ReadOnlyMemory<float>` with `[VectorStoreVector]` | SK connector expects its own vector types |
307
- | No `StorageName` on attributes | Set `StorageName` for snake_case columns | C# PascalCase won't match Postgres columns |
308
- | Creating collection without `EnsureCollectionExistsAsync` | Call before first upsert/search | Table and index won't exist |
309
- | Hardcoded embedding dimensions | Match `Dimensions` to your model (1536 for ada-002/3-small) | Mismatch causes insertion errors |
310
- | Using pgvector-dotnet `Vector` with SK | Use `ReadOnlyMemory<float>` | SK has its own type mapping layer |
@@ -1,125 +0,0 @@
1
- // ============================================================
2
- // SEMANTIC KERNEL + NEON PGVECTOR SETUP TEMPLATE
3
- // Generated by MORPH Framework
4
- // ============================================================
5
-
6
- using Microsoft.Extensions.DependencyInjection;
7
- using Microsoft.Extensions.VectorData;
8
- using Microsoft.SemanticKernel;
9
- using Microsoft.SemanticKernel.Connectors.PgVector;
10
- using Microsoft.SemanticKernel.Connectors.OpenAI;
11
- using Microsoft.SemanticKernel.Embeddings;
12
- using Npgsql;
13
-
14
- #nullable disable
15
-
16
- namespace {{NAMESPACE}}.Infrastructure.AI;
17
-
18
- // ============================================================
19
- // 1. DATA MODEL — annotated for Semantic Kernel vector store
20
- // ============================================================
21
-
22
- public class KnowledgeEntry
23
- {
24
- [VectorStoreKey(StorageName = "id")]
25
- public Guid Id { get; set; }
26
-
27
- [VectorStoreData(StorageName = "title")]
28
- public string Title { get; set; } = string.Empty;
29
-
30
- [VectorStoreData(StorageName = "content", IsFullTextSearchable = true)]
31
- public string Content { get; set; } = string.Empty;
32
-
33
- [VectorStoreData(StorageName = "source")]
34
- public string? Source { get; set; }
35
-
36
- [VectorStoreVector(Dimensions: 1536, DistanceFunction.CosineSimilarity, StorageName = "embedding")]
37
- public ReadOnlyMemory<float> Embedding { get; set; }
38
-
39
- [VectorStoreData(StorageName = "created_at")]
40
- public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow;
41
- }
42
-
43
- // ============================================================
44
- // 2. SERVICE REGISTRATION — add to Program.cs
45
- // ============================================================
46
-
47
- public static class SemanticKernelExtensions
48
- {
49
- public static IServiceCollection AddSemanticKernelWithNeon(
50
- this IServiceCollection services,
51
- string neonConnectionString,
52
- string openAiApiKey,
53
- string embeddingModel = "text-embedding-3-small")
54
- {
55
- // Neon PostgreSQL data source with pgvector
56
- services.AddSingleton<NpgsqlDataSource>(sp =>
57
- {
58
- var builder = new NpgsqlDataSourceBuilder(neonConnectionString);
59
- builder.UseVector();
60
- return builder.Build();
61
- });
62
-
63
- // Semantic Kernel with OpenAI + Postgres vector store
64
- services.AddKernel();
65
-
66
- services.AddOpenAITextEmbeddingGeneration(embeddingModel, openAiApiKey);
67
-
68
- services.AddPostgresVectorStore();
69
-
70
- // VectorStoreTextSearch for natural language queries
71
- services.AddSingleton<VectorStoreTextSearch<KnowledgeEntry>>(sp =>
72
- {
73
- var vectorStore = sp.GetRequiredService<IVectorStore>();
74
- var collection = vectorStore.GetCollection<Guid, KnowledgeEntry>("knowledge_entries");
75
- var embeddingGenerator = sp.GetRequiredService<ITextEmbeddingGenerationService>();
76
-
77
- return new VectorStoreTextSearch<KnowledgeEntry>(
78
- collection, embeddingGenerator);
79
- });
80
-
81
- return services;
82
- }
83
- }
84
-
85
- // ============================================================
86
- // SETUP INSTRUCTIONS
87
- // ============================================================
88
- //
89
- // 1. Install NuGet packages:
90
- //
91
- // dotnet add package Microsoft.SemanticKernel.Connectors.PgVector --prerelease
92
- // dotnet add package Microsoft.SemanticKernel.Connectors.OpenAI
93
- // dotnet add package Npgsql
94
- //
95
- // 2. Add to Program.cs:
96
- //
97
- // builder.Services.AddSemanticKernelWithNeon(
98
- // builder.Configuration.GetConnectionString("DefaultConnection")!,
99
- // builder.Configuration["OpenAI:ApiKey"]!);
100
- //
101
- // 3. Create the database table (Neon SQL):
102
- //
103
- // CREATE EXTENSION IF NOT EXISTS vector;
104
- // CREATE TABLE knowledge_entries (
105
- // id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
106
- // title TEXT NOT NULL,
107
- // content TEXT NOT NULL,
108
- // source TEXT,
109
- // embedding vector(1536),
110
- // created_at TIMESTAMPTZ DEFAULT now()
111
- // );
112
- // CREATE INDEX ON knowledge_entries USING hnsw (embedding vector_cosine_ops);
113
- //
114
- // 4. Use in a service or MAF agent:
115
- //
116
- // public class SearchService(VectorStoreTextSearch<KnowledgeEntry> search)
117
- // {
118
- // public async Task<IReadOnlyList<KnowledgeEntry>> SearchAsync(string query)
119
- // {
120
- // var results = await search.SearchAsync(query, new() { Top = 5 });
121
- // return await results.Results.Select(r => r.Record).ToListAsync();
122
- // }
123
- // }
124
- //
125
- // ============================================================