@polymorphism-tech/morph-spec 7.0.0 → 7.1.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/README.md +367 -367
- package/framework/agents.json +132 -127
- package/framework/rubrics/backend.json +41 -36
- package/framework/rules/ai-agents-standards.md +45 -15
- package/framework/rules/neon-standards.md +25 -26
- package/framework/standards/STANDARDS.json +1729 -1133
- package/framework/standards/ai-agents/agent-session.md +160 -0
- package/framework/standards/ai-agents/batching.md +193 -0
- package/framework/standards/ai-agents/context-providers.md +188 -0
- package/framework/standards/ai-agents/mcp-server.md +250 -0
- package/framework/standards/ai-agents/mcp-tools.md +197 -0
- package/framework/standards/ai-agents/middleware-patterns.md +207 -0
- package/framework/standards/ai-agents/modalities-image-gen.md +173 -0
- package/framework/standards/ai-agents/modalities-speech-to-text.md +171 -0
- package/framework/standards/ai-agents/modalities-text-to-speech.md +181 -0
- package/framework/standards/ai-agents/modalities-vision.md +201 -0
- package/framework/standards/ai-agents/multi-agent-patterns.md +155 -0
- package/framework/standards/ai-agents/production.md +114 -258
- package/framework/standards/ai-agents/prompts-in-db.md +214 -0
- package/framework/standards/ai-agents/providers/model-registry.md +158 -0
- package/framework/standards/ai-agents/rag-custom-pgvector.md +243 -0
- package/framework/standards/ai-agents/service-tier-flex.md +135 -0
- package/framework/standards/ai-agents/setup.md +142 -316
- package/framework/standards/ai-agents/structured-output.md +249 -0
- package/framework/standards/ai-agents/sweet-spot.md +109 -0
- package/framework/standards/ai-agents/workflows.md +114 -284
- package/framework/standards/architecture/vertical-slice/vertical-slice.md +433 -429
- package/framework/standards/backend/api/validation.md +92 -88
- package/framework/standards/backend/database/postgresql/database.md +336 -332
- package/framework/standards/backend/database/vector-search-rag.md +599 -541
- package/framework/standards/backend/dotnet/fluent-validation-vsa.md +4 -0
- package/framework/standards/backend/dotnet/result-pattern.md +4 -0
- package/framework/standards/backend/dotnet/scalar-ui.md +176 -172
- package/framework/standards/backend/dotnet/scrutor.md +4 -0
- package/framework/standards/backend/dotnet/vsa-handler-patterns.md +278 -274
- package/framework/standards/backend/integrations/asaas/asaas-api.md +260 -216
- package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +354 -350
- package/framework/standards/backend/integrations/neon-auth/neon-auth.md +30 -19
- package/framework/standards/backend/integrations/resend/resend-email.md +400 -385
- package/framework/standards/data/nosql/cache/redis.md +106 -97
- package/framework/standards/data/nosql/cosmos-db.md +127 -118
- package/framework/standards/data/vector-search/rag-chunking.md +113 -104
- package/framework/standards/frontend/design-system/ai-image-generation.md +4 -0
- package/framework/standards/frontend/design-system/animations.md +164 -160
- package/framework/standards/frontend/design-system/naming.md +68 -64
- package/framework/standards/frontend/gsap/gsap-core.md +14 -8
- package/framework/standards/frontend/gsap/gsap-plugins.md +26 -21
- package/framework/standards/frontend/gsap/gsap-scrolltrigger.md +9 -7
- package/framework/standards/frontend/nextjs/app-router.md +127 -123
- package/framework/standards/frontend/nextjs/components.md +136 -132
- package/framework/standards/frontend/nextjs/data-fetching.md +130 -126
- package/framework/standards/frontend/nextjs/forms.md +134 -128
- package/framework/standards/frontend/nextjs/motion-patterns.md +328 -322
- package/framework/standards/frontend/nextjs/naming-conventions.md +71 -67
- package/framework/standards/frontend/nextjs/nextjs-patterns.md +229 -225
- package/framework/standards/frontend/nextjs/project-structure.md +106 -102
- package/framework/standards/frontend/nextjs/state-management.md +76 -72
- package/framework/standards/frontend/nextjs/testing.md +115 -111
- package/framework/standards/infrastructure/docker/easypanel-deploy.md +196 -192
- package/framework/standards/infrastructure/neon/neon-pgrag.md +6 -2
- package/framework/standards/infrastructure/neon/neon-pgvector.md +4 -0
- package/framework/standards/infrastructure/neon/neon-setup.md +5 -1
- package/framework/standards/integration/mcp/mcp-tools.md +471 -462
- package/framework/templates/REGISTRY.json +1745 -1239
- package/framework/templates/code/dotnet/ai-agents/AgentBootstrap.cs.template +39 -0
- package/framework/templates/code/dotnet/ai-agents/EmbeddingService.cs.template +68 -0
- package/framework/templates/code/dotnet/ai-agents/ModelRegistry.cs.template +117 -0
- package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +95 -0
- package/framework/templates/code/dotnet/ai-agents/ResilientToolMiddleware.cs.template +61 -0
- package/framework/templates/code/dotnet/ai-agents/VectorSearchTool.cs.template +107 -0
- package/framework/templates/code/dotnet/ai-agents/model-registry.json.template +58 -0
- package/framework/templates/code/sql/ai-agents/agent-prompts-migration.sql.template +58 -0
- package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -0
- package/package.json +89 -83
- package/src/core/templates/template-registry.js +289 -256
- package/src/lib/generators/context-generator.js +516 -518
- package/src/lib/generators/scaffold-generator.js +23 -26
- package/framework/standards/infrastructure/neon/neon-semantic-kernel.md +0 -310
- 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
|
-
|
|
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
|
-
{
|
|
73
|
-
{
|
|
74
|
-
{
|
|
75
|
-
{
|
|
76
|
-
{
|
|
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
|
-
/**
|
|
77
|
+
/** .NET feature-level files (entity, errors). */
|
|
80
78
|
const DOTNET_FEATURE_TEMPLATES = [
|
|
81
|
-
{
|
|
82
|
-
{
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
168
|
+
templateId: 'nextjs-page',
|
|
171
169
|
outputFn: (kebab) => join('app', '(dashboard)', kebab, 'page.tsx'),
|
|
172
170
|
},
|
|
173
171
|
{
|
|
174
|
-
|
|
172
|
+
templateId: 'nextjs-client-component',
|
|
175
173
|
outputFn: (kebab) => join('features', kebab, 'components', `${kebab}-list.tsx`),
|
|
176
174
|
},
|
|
177
175
|
{
|
|
178
|
-
|
|
176
|
+
templateId: 'nextjs-feature-form',
|
|
179
177
|
outputFn: (kebab) => join('features', kebab, 'components', `create-${kebab}-form.tsx`),
|
|
180
178
|
},
|
|
181
179
|
{
|
|
182
|
-
|
|
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 =
|
|
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
|
-
// ============================================================
|