graphile-llm 0.2.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.
Files changed (43) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +193 -0
  3. package/__tests__/graphile-llm.test.d.ts +1 -0
  4. package/__tests__/graphile-llm.test.js +721 -0
  5. package/chat.d.ts +37 -0
  6. package/chat.js +105 -0
  7. package/embedder.d.ts +35 -0
  8. package/embedder.js +79 -0
  9. package/esm/__tests__/graphile-llm.test.d.ts +1 -0
  10. package/esm/__tests__/graphile-llm.test.js +683 -0
  11. package/esm/chat.d.ts +37 -0
  12. package/esm/chat.js +97 -0
  13. package/esm/embedder.d.ts +35 -0
  14. package/esm/embedder.js +71 -0
  15. package/esm/index.d.ts +39 -0
  16. package/esm/index.js +42 -0
  17. package/esm/plugins/llm-module-plugin.d.ts +38 -0
  18. package/esm/plugins/llm-module-plugin.js +82 -0
  19. package/esm/plugins/rag-plugin.d.ts +36 -0
  20. package/esm/plugins/rag-plugin.js +341 -0
  21. package/esm/plugins/text-mutation-plugin.d.ts +44 -0
  22. package/esm/plugins/text-mutation-plugin.js +191 -0
  23. package/esm/plugins/text-search-plugin.d.ts +41 -0
  24. package/esm/plugins/text-search-plugin.js +163 -0
  25. package/esm/preset.d.ts +55 -0
  26. package/esm/preset.js +74 -0
  27. package/esm/types.d.ts +173 -0
  28. package/esm/types.js +6 -0
  29. package/index.d.ts +39 -0
  30. package/index.js +56 -0
  31. package/package.json +76 -0
  32. package/plugins/llm-module-plugin.d.ts +38 -0
  33. package/plugins/llm-module-plugin.js +85 -0
  34. package/plugins/rag-plugin.d.ts +36 -0
  35. package/plugins/rag-plugin.js +344 -0
  36. package/plugins/text-mutation-plugin.d.ts +44 -0
  37. package/plugins/text-mutation-plugin.js +194 -0
  38. package/plugins/text-search-plugin.d.ts +41 -0
  39. package/plugins/text-search-plugin.js +166 -0
  40. package/preset.d.ts +55 -0
  41. package/preset.js +77 -0
  42. package/types.d.ts +173 -0
  43. package/types.js +7 -0
@@ -0,0 +1,163 @@
1
+ /**
2
+ * LlmTextSearchPlugin
3
+ *
4
+ * Adds a `text: String` field to `VectorNearbyInput` when the LLM plugin
5
+ * is enabled. This allows clients to pass natural language text instead of
6
+ * raw float vectors for similarity search — the plugin converts text to
7
+ * vectors server-side using the configured embedder.
8
+ *
9
+ * This mirrors the graphile-postgis pattern where `WithinDistanceInput`
10
+ * accepts a compound input (point + distance) and the plugin handles
11
+ * the conversion to SQL internally.
12
+ *
13
+ * The `text` field is mutually exclusive with `vector`: clients provide
14
+ * one or the other. When `text` is provided, the plugin embeds it and
15
+ * injects the resulting vector into the normal pgvector pipeline.
16
+ *
17
+ * Runtime embedding for query filters uses the v4-style resolver wrapping
18
+ * approach (same as graphile-upload-plugin). When a connection query's
19
+ * `where` argument includes a VectorNearbyInput with `text`, the resolver
20
+ * wrapper embeds the text and replaces it with the resulting vector before
21
+ * the plan executes.
22
+ *
23
+ * If the embedder is not configured, the `text` field is still registered
24
+ * (so the schema is stable) but will return a clear error at execution time.
25
+ */
26
+ /**
27
+ * Check if a codec has any pgvector `vector` columns.
28
+ */
29
+ function hasVectorColumns(pgCodec) {
30
+ if (!pgCodec?.attributes)
31
+ return false;
32
+ for (const attribute of Object.values(pgCodec.attributes)) {
33
+ if (attribute.codec?.name === 'vector')
34
+ return true;
35
+ }
36
+ return false;
37
+ }
38
+ /**
39
+ * Recursively walk a `where` argument object and embed any VectorNearbyInput
40
+ * values that have `text` instead of `vector`.
41
+ */
42
+ async function embedTextInWhere(obj, embedder) {
43
+ if (!obj || typeof obj !== 'object')
44
+ return;
45
+ const pending = [];
46
+ for (const key of Object.keys(obj)) {
47
+ const value = obj[key];
48
+ if (!value || typeof value !== 'object')
49
+ continue;
50
+ // Detect VectorNearbyInput shape: has `text` and no `vector`
51
+ if ('text' in value && typeof value.text === 'string' && !value.vector) {
52
+ pending.push((async () => {
53
+ const startTime = Date.now();
54
+ const vector = await embedder(value.text);
55
+ const latencyMs = Date.now() - startTime;
56
+ console.log(`[graphile-llm] Search embed: field=${key}, dims=${vector.length}, latency=${latencyMs}ms`);
57
+ // Replace text with vector
58
+ value.vector = vector;
59
+ delete value.text;
60
+ })());
61
+ continue;
62
+ }
63
+ // Recurse into nested filter objects (AND, OR, etc.)
64
+ if (!Array.isArray(value)) {
65
+ pending.push(embedTextInWhere(value, embedder));
66
+ }
67
+ else {
68
+ // Handle arrays (e.g. AND: [...], OR: [...])
69
+ for (const item of value) {
70
+ pending.push(embedTextInWhere(item, embedder));
71
+ }
72
+ }
73
+ }
74
+ if (pending.length > 0) {
75
+ await Promise.all(pending);
76
+ }
77
+ }
78
+ /**
79
+ * Creates the LlmTextSearchPlugin.
80
+ *
81
+ * Hooks into VectorNearbyInput to add a `text` field alongside the
82
+ * existing `vector` field. When a user provides `text`, the plugin's
83
+ * resolver wrapper embeds it before passing to pgvector.
84
+ */
85
+ export function createLlmTextSearchPlugin() {
86
+ return {
87
+ name: 'LlmTextSearchPlugin',
88
+ version: '0.1.0',
89
+ description: 'Adds text-to-vector embedding support on VectorNearbyInput filter fields',
90
+ after: [
91
+ 'LlmModulePlugin',
92
+ 'UnifiedSearchPlugin',
93
+ 'VectorCodecPlugin',
94
+ ],
95
+ schema: {
96
+ hooks: {
97
+ /**
98
+ * Add the `text: String` field to VectorNearbyInput.
99
+ *
100
+ * We intercept VectorNearbyInput specifically and add a `text` field.
101
+ * The field is optional — clients provide either `text` or `vector`.
102
+ */
103
+ GraphQLInputObjectType_fields(fields, build, context) {
104
+ const { scope: { inputObjectTypeName }, } = context;
105
+ if (inputObjectTypeName !== 'VectorNearbyInput') {
106
+ return fields;
107
+ }
108
+ const { graphql: { GraphQLString }, } = build;
109
+ return build.extend(fields, {
110
+ text: {
111
+ type: GraphQLString,
112
+ description: 'Natural language text to embed server-side for similarity search. ' +
113
+ 'Mutually exclusive with `vector` — provide one or the other. ' +
114
+ 'Requires the LLM plugin to be configured with an embedding provider.',
115
+ },
116
+ }, 'LlmTextSearchPlugin adding text field to VectorNearbyInput');
117
+ },
118
+ /**
119
+ * Wrap connection query resolvers to intercept `where` arguments that
120
+ * contain VectorNearbyInput with `text`, embed the text, and replace
121
+ * it with the resulting vector before the plan executes.
122
+ *
123
+ * Uses the same v4-style resolver wrapping pattern as graphile-upload-plugin
124
+ * and graphile-bucket-provisioner-plugin.
125
+ */
126
+ GraphQLObjectType_fields_field(field, build, context) {
127
+ const { scope: { isRootQuery, pgCodec }, } = context;
128
+ // Only wrap root query fields on tables with vector columns
129
+ if (!isRootQuery || !pgCodec || !hasVectorColumns(pgCodec)) {
130
+ return field;
131
+ }
132
+ const embedder = build.llmEmbedder;
133
+ if (!embedder)
134
+ return field;
135
+ const defaultResolver = (obj) => obj[context.scope.fieldName];
136
+ const { resolve: oldResolve = defaultResolver, ...rest } = field;
137
+ return {
138
+ ...rest,
139
+ async resolve(source, args, graphqlContext, info) {
140
+ // If the query has a `where` argument, check for text fields
141
+ if (args?.where) {
142
+ await embedTextInWhere(args.where, embedder);
143
+ }
144
+ // Also handle `filter` for relay-style connections
145
+ if (args?.filter) {
146
+ await embedTextInWhere(args.filter, embedder);
147
+ }
148
+ return oldResolve(source, args, graphqlContext, info);
149
+ },
150
+ };
151
+ },
152
+ finalize(schema, build) {
153
+ const embedder = build.llmEmbedder;
154
+ if (!embedder) {
155
+ console.log('[graphile-llm] No embedder available — text field on VectorNearbyInput ' +
156
+ 'will return errors if used. Configure an embedding provider to enable.');
157
+ }
158
+ return schema;
159
+ },
160
+ },
161
+ },
162
+ };
163
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * GraphileLlmPreset
3
+ *
4
+ * A preset that bundles all LLM plugins for PostGraphile v5.
5
+ * This is the recommended entry point — add it to your preset's `extends` array.
6
+ *
7
+ * When enabled, this preset:
8
+ * - Resolves an embedder from configuration (llm_module, env vars, or preset options)
9
+ * - Adds a `text: String` field to `VectorNearbyInput` for text-based vector search
10
+ * - Adds `{column}Text: String` companion fields on mutation inputs for vector columns
11
+ * - Logs token usage to console (metering integration deferred to billing system)
12
+ *
13
+ * This preset is standalone — it is NOT included in ConstructivePreset by default.
14
+ * Projects that want LLM features opt in by adding it to their preset.
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * import { GraphileLlmPreset } from 'graphile-llm';
19
+ *
20
+ * const preset = {
21
+ * extends: [
22
+ * ConstructivePreset,
23
+ * GraphileLlmPreset(),
24
+ * ],
25
+ * };
26
+ * ```
27
+ *
28
+ * @example With explicit embedder configuration:
29
+ * ```typescript
30
+ * import { GraphileLlmPreset } from 'graphile-llm';
31
+ *
32
+ * const preset = {
33
+ * extends: [
34
+ * ConstructivePreset,
35
+ * GraphileLlmPreset({
36
+ * defaultEmbedder: {
37
+ * provider: 'ollama',
38
+ * model: 'nomic-embed-text',
39
+ * baseUrl: 'http://localhost:11434',
40
+ * },
41
+ * }),
42
+ * ],
43
+ * };
44
+ * ```
45
+ */
46
+ import type { GraphileConfig } from 'graphile-config';
47
+ import type { GraphileLlmOptions } from './types';
48
+ /**
49
+ * Creates a preset that includes all LLM plugins.
50
+ *
51
+ * @param options - Configuration options for the LLM plugin
52
+ * @returns A GraphileConfig.Preset to add to your extends array
53
+ */
54
+ export declare function GraphileLlmPreset(options?: GraphileLlmOptions): GraphileConfig.Preset;
55
+ export default GraphileLlmPreset;
package/esm/preset.js ADDED
@@ -0,0 +1,74 @@
1
+ /**
2
+ * GraphileLlmPreset
3
+ *
4
+ * A preset that bundles all LLM plugins for PostGraphile v5.
5
+ * This is the recommended entry point — add it to your preset's `extends` array.
6
+ *
7
+ * When enabled, this preset:
8
+ * - Resolves an embedder from configuration (llm_module, env vars, or preset options)
9
+ * - Adds a `text: String` field to `VectorNearbyInput` for text-based vector search
10
+ * - Adds `{column}Text: String` companion fields on mutation inputs for vector columns
11
+ * - Logs token usage to console (metering integration deferred to billing system)
12
+ *
13
+ * This preset is standalone — it is NOT included in ConstructivePreset by default.
14
+ * Projects that want LLM features opt in by adding it to their preset.
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * import { GraphileLlmPreset } from 'graphile-llm';
19
+ *
20
+ * const preset = {
21
+ * extends: [
22
+ * ConstructivePreset,
23
+ * GraphileLlmPreset(),
24
+ * ],
25
+ * };
26
+ * ```
27
+ *
28
+ * @example With explicit embedder configuration:
29
+ * ```typescript
30
+ * import { GraphileLlmPreset } from 'graphile-llm';
31
+ *
32
+ * const preset = {
33
+ * extends: [
34
+ * ConstructivePreset,
35
+ * GraphileLlmPreset({
36
+ * defaultEmbedder: {
37
+ * provider: 'ollama',
38
+ * model: 'nomic-embed-text',
39
+ * baseUrl: 'http://localhost:11434',
40
+ * },
41
+ * }),
42
+ * ],
43
+ * };
44
+ * ```
45
+ */
46
+ import { createLlmModulePlugin } from './plugins/llm-module-plugin';
47
+ import { createLlmTextSearchPlugin } from './plugins/text-search-plugin';
48
+ import { createLlmTextMutationPlugin } from './plugins/text-mutation-plugin';
49
+ import { createLlmRagPlugin } from './plugins/rag-plugin';
50
+ /**
51
+ * Creates a preset that includes all LLM plugins.
52
+ *
53
+ * @param options - Configuration options for the LLM plugin
54
+ * @returns A GraphileConfig.Preset to add to your extends array
55
+ */
56
+ export function GraphileLlmPreset(options = {}) {
57
+ const { enableTextSearch = true, enableTextMutations = true, enableRag = false, ragDefaults, } = options;
58
+ const plugins = [
59
+ createLlmModulePlugin(options),
60
+ ];
61
+ if (enableTextSearch) {
62
+ plugins.push(createLlmTextSearchPlugin());
63
+ }
64
+ if (enableTextMutations) {
65
+ plugins.push(createLlmTextMutationPlugin());
66
+ }
67
+ if (enableRag) {
68
+ plugins.push(createLlmRagPlugin(ragDefaults));
69
+ }
70
+ return {
71
+ plugins,
72
+ };
73
+ }
74
+ export default GraphileLlmPreset;
package/esm/types.d.ts ADDED
@@ -0,0 +1,173 @@
1
+ /**
2
+ * graphile-llm Types
3
+ *
4
+ * Shared type definitions for the LLM plugin.
5
+ */
6
+ /**
7
+ * A function that converts text into a vector embedding.
8
+ */
9
+ export type EmbedderFunction = (text: string) => Promise<number[]>;
10
+ /**
11
+ * Configuration for an embedding provider.
12
+ */
13
+ export interface EmbedderConfig {
14
+ /** Provider name: 'ollama', 'openai', or 'custom' */
15
+ provider: string;
16
+ /** Model identifier (e.g. 'nomic-embed-text', 'text-embedding-3-small') */
17
+ model?: string;
18
+ /** Base URL for the provider (e.g. 'http://localhost:11434' for Ollama) */
19
+ baseUrl?: string;
20
+ /** API key for providers that require authentication (e.g. OpenAI) */
21
+ apiKey?: string;
22
+ }
23
+ /**
24
+ * A single message in a chat conversation.
25
+ */
26
+ export interface ChatMessage {
27
+ role: 'system' | 'user' | 'assistant';
28
+ content: string;
29
+ }
30
+ /**
31
+ * Options for a chat completion request.
32
+ */
33
+ export interface ChatOptions {
34
+ /** Maximum tokens to generate */
35
+ maxTokens?: number;
36
+ /** Temperature for sampling (0 = deterministic, 1 = creative) */
37
+ temperature?: number;
38
+ }
39
+ /**
40
+ * A function that sends messages to a chat completion provider and returns the response.
41
+ */
42
+ export type ChatFunction = (messages: ChatMessage[], options?: ChatOptions) => Promise<string>;
43
+ /**
44
+ * Configuration for a chat completion provider.
45
+ */
46
+ export interface ChatConfig {
47
+ /** Provider name: 'ollama', 'openai', or 'custom' */
48
+ provider: string;
49
+ /** Model identifier (e.g. 'llama3', 'gpt-4o') */
50
+ model?: string;
51
+ /** Base URL for the provider */
52
+ baseUrl?: string;
53
+ /** API key for providers that require authentication */
54
+ apiKey?: string;
55
+ }
56
+ /**
57
+ * The shape of the `llm_module` data stored in `services_public.api_modules`.
58
+ *
59
+ * This is the per-database configuration that controls which LLM provider
60
+ * and models are available for that API.
61
+ */
62
+ export interface LlmModuleData {
63
+ /** Embedding provider: 'ollama', 'openai', or 'custom' */
64
+ embedding_provider: string;
65
+ /** Embedding model identifier */
66
+ embedding_model?: string;
67
+ /** Base URL for the embedding provider */
68
+ embedding_base_url?: string;
69
+ /** Number of dimensions the embedding model produces */
70
+ embedding_dimensions?: number;
71
+ /** Chat/completion provider (for RAG/conversation features) */
72
+ chat_provider?: string;
73
+ /** Chat model identifier */
74
+ chat_model?: string;
75
+ /** Base URL for the chat provider */
76
+ chat_base_url?: string;
77
+ /** API key reference (e.g. 'vault://openai-key' or env var name) */
78
+ api_key_ref?: string;
79
+ /** Rate limit: requests per minute */
80
+ rate_limit_rpm?: number;
81
+ /** Maximum tokens per request */
82
+ max_tokens_per_request?: number;
83
+ /** Default number of context items for RAG queries */
84
+ rag_context_limit?: number;
85
+ }
86
+ /**
87
+ * Default configuration for RAG (Retrieval-Augmented Generation) queries.
88
+ */
89
+ export interface RagDefaults {
90
+ /**
91
+ * Default number of context items to feed into the RAG prompt.
92
+ * Per-query `contextLimit` overrides this.
93
+ * @default 5
94
+ */
95
+ contextLimit?: number;
96
+ /**
97
+ * Default maximum tokens for the chat completion response.
98
+ * @default 4000
99
+ */
100
+ maxTokens?: number;
101
+ /**
102
+ * Default minimum similarity threshold (0..1).
103
+ * Only chunks with similarity >= this threshold are included.
104
+ * Converted to max distance internally (1 - minSimilarity for cosine).
105
+ * @default 0 (no threshold)
106
+ */
107
+ minSimilarity?: number;
108
+ /**
109
+ * Default system prompt prepended to RAG context.
110
+ * Can be overridden per-query.
111
+ */
112
+ systemPrompt?: string;
113
+ }
114
+ /**
115
+ * Info about a chunk-aware table discovered during schema build.
116
+ * Parsed from the @hasChunks smart tag on the parent table's codec.
117
+ */
118
+ export interface ChunkTableInfo {
119
+ /** Parent table codec name */
120
+ parentCodecName: string;
121
+ /** Schema of the chunks table (or null for public/default) */
122
+ chunksSchema: string | null;
123
+ /** Name of the chunks table */
124
+ chunksTableName: string;
125
+ /** FK column on chunks table pointing to parent */
126
+ parentFkField: string;
127
+ /** PK column on parent table */
128
+ parentPkField: string;
129
+ /** Embedding vector column on chunks table */
130
+ embeddingField: string;
131
+ /** Text content column on chunks table (the actual chunk text) */
132
+ contentField: string;
133
+ }
134
+ /**
135
+ * Options for the GraphileLlmPreset.
136
+ */
137
+ export interface GraphileLlmOptions {
138
+ /**
139
+ * Default embedding provider when no llm_module is configured.
140
+ * Useful for development/testing without requiring api_modules setup.
141
+ * @default undefined (requires llm_module to be configured)
142
+ */
143
+ defaultEmbedder?: EmbedderConfig;
144
+ /**
145
+ * Default chat completion provider when no llm_module is configured.
146
+ * Required for RAG queries.
147
+ * @default undefined
148
+ */
149
+ defaultChatCompleter?: ChatConfig;
150
+ /**
151
+ * Whether to add `text` field to VectorNearbyInput for text-based vector search.
152
+ * @default true
153
+ */
154
+ enableTextSearch?: boolean;
155
+ /**
156
+ * Whether to add `*Text` companion fields on mutation inputs for vector columns.
157
+ * @default true
158
+ */
159
+ enableTextMutations?: boolean;
160
+ /**
161
+ * Whether to enable RAG (Retrieval-Augmented Generation) query support.
162
+ * When enabled, detects tables with @hasChunks smart tag and adds:
163
+ * - `ragQuery` root query field for context-aware LLM answers
164
+ * - `embedText` root query field for text-to-vector conversion
165
+ * @default false
166
+ */
167
+ enableRag?: boolean;
168
+ /**
169
+ * Default configuration for RAG queries.
170
+ * Individual queries can override these values.
171
+ */
172
+ ragDefaults?: RagDefaults;
173
+ }
package/esm/types.js ADDED
@@ -0,0 +1,6 @@
1
+ /**
2
+ * graphile-llm Types
3
+ *
4
+ * Shared type definitions for the LLM plugin.
5
+ */
6
+ export {};
package/index.d.ts ADDED
@@ -0,0 +1,39 @@
1
+ /**
2
+ * graphile-llm — LLM Integration Plugin for PostGraphile v5
3
+ *
4
+ * Server-side text-to-vector embedding, text companion fields for pgvector
5
+ * columns, and RAG (Retrieval-Augmented Generation) query support.
6
+ *
7
+ * Moves the embedding logic from the client (CLI --auto-embed) into the
8
+ * Graphile server layer so clients work with text/prompts instead of raw
9
+ * float vectors.
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * import { GraphileLlmPreset } from 'graphile-llm';
14
+ *
15
+ * const preset = {
16
+ * extends: [
17
+ * GraphileLlmPreset({
18
+ * defaultEmbedder: {
19
+ * provider: 'ollama',
20
+ * model: 'nomic-embed-text',
21
+ * },
22
+ * defaultChatCompleter: {
23
+ * provider: 'ollama',
24
+ * model: 'llama3',
25
+ * },
26
+ * enableRag: true,
27
+ * }),
28
+ * ],
29
+ * };
30
+ * ```
31
+ */
32
+ export { GraphileLlmPreset } from './preset';
33
+ export { createLlmModulePlugin } from './plugins/llm-module-plugin';
34
+ export { createLlmTextSearchPlugin } from './plugins/text-search-plugin';
35
+ export { createLlmTextMutationPlugin } from './plugins/text-mutation-plugin';
36
+ export { createLlmRagPlugin } from './plugins/rag-plugin';
37
+ export { buildEmbedder, buildEmbedderFromModule, buildEmbedderFromEnv, } from './embedder';
38
+ export { buildChatCompleter, buildChatCompleterFromModule, buildChatCompleterFromEnv, } from './chat';
39
+ export type { EmbedderFunction, EmbedderConfig, ChatFunction, ChatConfig, ChatMessage, ChatOptions, LlmModuleData, GraphileLlmOptions, RagDefaults, ChunkTableInfo, } from './types';
package/index.js ADDED
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ /**
3
+ * graphile-llm — LLM Integration Plugin for PostGraphile v5
4
+ *
5
+ * Server-side text-to-vector embedding, text companion fields for pgvector
6
+ * columns, and RAG (Retrieval-Augmented Generation) query support.
7
+ *
8
+ * Moves the embedding logic from the client (CLI --auto-embed) into the
9
+ * Graphile server layer so clients work with text/prompts instead of raw
10
+ * float vectors.
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * import { GraphileLlmPreset } from 'graphile-llm';
15
+ *
16
+ * const preset = {
17
+ * extends: [
18
+ * GraphileLlmPreset({
19
+ * defaultEmbedder: {
20
+ * provider: 'ollama',
21
+ * model: 'nomic-embed-text',
22
+ * },
23
+ * defaultChatCompleter: {
24
+ * provider: 'ollama',
25
+ * model: 'llama3',
26
+ * },
27
+ * enableRag: true,
28
+ * }),
29
+ * ],
30
+ * };
31
+ * ```
32
+ */
33
+ Object.defineProperty(exports, "__esModule", { value: true });
34
+ exports.buildChatCompleterFromEnv = exports.buildChatCompleterFromModule = exports.buildChatCompleter = exports.buildEmbedderFromEnv = exports.buildEmbedderFromModule = exports.buildEmbedder = exports.createLlmRagPlugin = exports.createLlmTextMutationPlugin = exports.createLlmTextSearchPlugin = exports.createLlmModulePlugin = exports.GraphileLlmPreset = void 0;
35
+ // Preset (recommended entry point)
36
+ var preset_1 = require("./preset");
37
+ Object.defineProperty(exports, "GraphileLlmPreset", { enumerable: true, get: function () { return preset_1.GraphileLlmPreset; } });
38
+ // Individual plugins
39
+ var llm_module_plugin_1 = require("./plugins/llm-module-plugin");
40
+ Object.defineProperty(exports, "createLlmModulePlugin", { enumerable: true, get: function () { return llm_module_plugin_1.createLlmModulePlugin; } });
41
+ var text_search_plugin_1 = require("./plugins/text-search-plugin");
42
+ Object.defineProperty(exports, "createLlmTextSearchPlugin", { enumerable: true, get: function () { return text_search_plugin_1.createLlmTextSearchPlugin; } });
43
+ var text_mutation_plugin_1 = require("./plugins/text-mutation-plugin");
44
+ Object.defineProperty(exports, "createLlmTextMutationPlugin", { enumerable: true, get: function () { return text_mutation_plugin_1.createLlmTextMutationPlugin; } });
45
+ var rag_plugin_1 = require("./plugins/rag-plugin");
46
+ Object.defineProperty(exports, "createLlmRagPlugin", { enumerable: true, get: function () { return rag_plugin_1.createLlmRagPlugin; } });
47
+ // Embedder utilities
48
+ var embedder_1 = require("./embedder");
49
+ Object.defineProperty(exports, "buildEmbedder", { enumerable: true, get: function () { return embedder_1.buildEmbedder; } });
50
+ Object.defineProperty(exports, "buildEmbedderFromModule", { enumerable: true, get: function () { return embedder_1.buildEmbedderFromModule; } });
51
+ Object.defineProperty(exports, "buildEmbedderFromEnv", { enumerable: true, get: function () { return embedder_1.buildEmbedderFromEnv; } });
52
+ // Chat completion utilities
53
+ var chat_1 = require("./chat");
54
+ Object.defineProperty(exports, "buildChatCompleter", { enumerable: true, get: function () { return chat_1.buildChatCompleter; } });
55
+ Object.defineProperty(exports, "buildChatCompleterFromModule", { enumerable: true, get: function () { return chat_1.buildChatCompleterFromModule; } });
56
+ Object.defineProperty(exports, "buildChatCompleterFromEnv", { enumerable: true, get: function () { return chat_1.buildChatCompleterFromEnv; } });
package/package.json ADDED
@@ -0,0 +1,76 @@
1
+ {
2
+ "name": "graphile-llm",
3
+ "version": "0.2.0",
4
+ "description": "LLM integration plugin for PostGraphile v5 — server-side text-to-vector embedding and text companion fields for pgvector columns",
5
+ "author": "Constructive <developers@constructive.io>",
6
+ "homepage": "https://github.com/constructive-io/constructive",
7
+ "license": "MIT",
8
+ "main": "index.js",
9
+ "module": "esm/index.js",
10
+ "types": "index.d.ts",
11
+ "scripts": {
12
+ "clean": "makage clean",
13
+ "prepack": "npm run build",
14
+ "build": "makage build",
15
+ "build:dev": "makage build --dev",
16
+ "lint": "eslint . --fix",
17
+ "test": "jest --forceExit",
18
+ "test:watch": "jest --watch"
19
+ },
20
+ "publishConfig": {
21
+ "access": "public",
22
+ "directory": "dist"
23
+ },
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "https://github.com/constructive-io/constructive"
27
+ },
28
+ "bugs": {
29
+ "url": "https://github.com/constructive-io/constructive/issues"
30
+ },
31
+ "dependencies": {
32
+ "@agentic-kit/ollama": "^1.0.3"
33
+ },
34
+ "peerDependencies": {
35
+ "@dataplan/pg": "1.0.0",
36
+ "grafast": "1.0.0",
37
+ "graphile-build": "5.0.0",
38
+ "graphile-build-pg": "5.0.0",
39
+ "graphile-config": "1.0.0",
40
+ "graphile-search": "workspace:^",
41
+ "graphile-utils": "5.0.0",
42
+ "graphql": "16.13.0",
43
+ "pg-sql2": "5.0.0",
44
+ "postgraphile": "5.0.0"
45
+ },
46
+ "peerDependenciesMeta": {
47
+ "graphile-search": {
48
+ "optional": true
49
+ },
50
+ "graphile-utils": {
51
+ "optional": true
52
+ }
53
+ },
54
+ "devDependencies": {
55
+ "@types/node": "^22.19.11",
56
+ "graphile-connection-filter": "^1.5.1",
57
+ "graphile-search": "^1.7.1",
58
+ "graphile-test": "^4.9.0",
59
+ "makage": "^0.3.0",
60
+ "pgsql-test": "^4.9.0"
61
+ },
62
+ "keywords": [
63
+ "postgraphile",
64
+ "graphile",
65
+ "constructive",
66
+ "plugin",
67
+ "llm",
68
+ "embedding",
69
+ "pgvector",
70
+ "rag",
71
+ "agentic-kit",
72
+ "ollama",
73
+ "openai"
74
+ ],
75
+ "gitHead": "e52a76e08338b00aa866f7cea3abb7414a3ac34c"
76
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * LlmModulePlugin
3
+ *
4
+ * Detects and loads the `llm_module` configuration from `services_public.api_modules`.
5
+ * Makes the resolved embedder available to other plugins via the build context.
6
+ *
7
+ * This plugin is the foundation that enables per-database LLM configuration.
8
+ * When an API has an `llm_module` configured, the embedder is resolved and
9
+ * stored on the build object for other plugins (text search, text mutations)
10
+ * to consume.
11
+ *
12
+ * Resolution order for the embedder:
13
+ * 1. `llm_module` from api_modules (per-database, loaded at schema build time)
14
+ * 2. `defaultEmbedder` from preset options (dev/testing fallback)
15
+ * 3. Environment variables (EMBEDDER_PROVIDER, EMBEDDER_MODEL, EMBEDDER_BASE_URL)
16
+ * 4. null — LLM features are disabled
17
+ */
18
+ import type { GraphileConfig } from 'graphile-config';
19
+ import type { EmbedderFunction, ChatFunction, GraphileLlmOptions } from '../types';
20
+ declare global {
21
+ namespace GraphileBuild {
22
+ interface Build {
23
+ /** The resolved embedder function, or null if LLM is not configured */
24
+ llmEmbedder: EmbedderFunction | null;
25
+ /** The resolved chat completion function, or null if not configured */
26
+ llmChatCompleter: ChatFunction | null;
27
+ }
28
+ }
29
+ namespace GraphileConfig {
30
+ interface Plugins {
31
+ LlmModulePlugin: true;
32
+ }
33
+ }
34
+ }
35
+ /**
36
+ * Creates the LlmModulePlugin with the given options.
37
+ */
38
+ export declare function createLlmModulePlugin(options?: GraphileLlmOptions): GraphileConfig.Plugin;