graphile-llm 1.21.7 → 1.21.8

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/embed-inputs.js CHANGED
@@ -134,13 +134,20 @@ function createEmbedIndex(graphql) {
134
134
  switch (rule.kind) {
135
135
  case 'nearby': {
136
136
  delete next[fieldName];
137
- if (isNonEmptyVector(value[rule.into]))
138
- return;
139
- if (typeof text !== 'string') {
140
- throw new Error(`${typeName}: provide either \`${rule.into}\` or \`${fieldName}\`.`);
137
+ const hasVector = isNonEmptyVector(value[rule.into]);
138
+ const hasText = typeof text === 'string';
139
+ if (hasVector && hasText) {
140
+ throw new Error(`${typeName}: provide exactly one of \`${rule.into}\` or \`${fieldName}\`, not both.`);
141
141
  }
142
- if (!embed)
142
+ if (!hasVector && !hasText) {
143
+ throw new Error(`${typeName}: provide exactly one of \`${rule.into}\` or \`${fieldName}\`; neither was given.`);
144
+ }
145
+ if (hasVector)
143
146
  return;
147
+ if (!embed) {
148
+ throw new Error(`${typeName}: \`${fieldName}\` needs an embedding provider and none is configured; ` +
149
+ `send \`${rule.into}\` instead.`);
150
+ }
144
151
  const vector = await embed(text);
145
152
  if (vector) {
146
153
  next[rule.into] = vector;
@@ -126,13 +126,20 @@ export function createEmbedIndex(graphql) {
126
126
  switch (rule.kind) {
127
127
  case 'nearby': {
128
128
  delete next[fieldName];
129
- if (isNonEmptyVector(value[rule.into]))
130
- return;
131
- if (typeof text !== 'string') {
132
- throw new Error(`${typeName}: provide either \`${rule.into}\` or \`${fieldName}\`.`);
129
+ const hasVector = isNonEmptyVector(value[rule.into]);
130
+ const hasText = typeof text === 'string';
131
+ if (hasVector && hasText) {
132
+ throw new Error(`${typeName}: provide exactly one of \`${rule.into}\` or \`${fieldName}\`, not both.`);
133
133
  }
134
- if (!embed)
134
+ if (!hasVector && !hasText) {
135
+ throw new Error(`${typeName}: provide exactly one of \`${rule.into}\` or \`${fieldName}\`; neither was given.`);
136
+ }
137
+ if (hasVector)
135
138
  return;
139
+ if (!embed) {
140
+ throw new Error(`${typeName}: \`${fieldName}\` needs an embedding provider and none is configured; ` +
141
+ `send \`${rule.into}\` instead.`);
142
+ }
136
143
  const vector = await embed(text);
137
144
  if (vector) {
138
145
  next[rule.into] = vector;
@@ -10,15 +10,15 @@
10
10
  * accepts a compound input (point + distance) and the plugin handles
11
11
  * the conversion to SQL internally.
12
12
  *
13
- * Clients provide `text` or `vector`; `vector` becomes optional. When both
14
- * are given the vector wins. `unifiedSearch` text is embedded too, so pgvector
13
+ * Clients provide exactly one of `text` or `vector`; `vector` becomes optional.
14
+ * Sending both, or neither, is an error. `unifiedSearch` text is embedded too, so pgvector
15
15
  * participates alongside the text adapters.
16
16
  *
17
17
  * The text is embedded while the operation is planned (see embed-inputs.ts),
18
18
  * before SQL is built.
19
19
  *
20
- * If no embedder is configured, `text` is ignored and `unifiedSearch` runs
21
- * on the text adapters only.
20
+ * If no embedder is configured, `text` on `VectorNearbyInput` is an error and
21
+ * `unifiedSearch` runs on the text adapters only.
22
22
  *
23
23
  * If the embedder returns null (e.g. quota exceeded when the metering
24
24
  * plugin is loaded), behavior depends on `onQuotaExceeded`:
@@ -10,15 +10,15 @@
10
10
  * accepts a compound input (point + distance) and the plugin handles
11
11
  * the conversion to SQL internally.
12
12
  *
13
- * Clients provide `text` or `vector`; `vector` becomes optional. When both
14
- * are given the vector wins. `unifiedSearch` text is embedded too, so pgvector
13
+ * Clients provide exactly one of `text` or `vector`; `vector` becomes optional.
14
+ * Sending both, or neither, is an error. `unifiedSearch` text is embedded too, so pgvector
15
15
  * participates alongside the text adapters.
16
16
  *
17
17
  * The text is embedded while the operation is planned (see embed-inputs.ts),
18
18
  * before SQL is built.
19
19
  *
20
- * If no embedder is configured, `text` is ignored and `unifiedSearch` runs
21
- * on the text adapters only.
20
+ * If no embedder is configured, `text` on `VectorNearbyInput` is an error and
21
+ * `unifiedSearch` runs on the text adapters only.
22
22
  *
23
23
  * If the embedder returns null (e.g. quota exceeded when the metering
24
24
  * plugin is loaded), behavior depends on `onQuotaExceeded`:
@@ -138,7 +138,7 @@ export function createLlmTextSearchPlugin(options = {}) {
138
138
  text: embedField({ kind: 'nearby', into: 'vector', onQuotaExceeded }, {
139
139
  type: GraphQLString,
140
140
  description: 'Natural language text to embed server-side for similarity search. ' +
141
- 'Provide `text` or `vector`; if both are given, `vector` is used. ' +
141
+ 'Provide exactly one of `text` or `vector`; sending both or neither is an error. ' +
142
142
  'Requires the LLM plugin to be configured with an embedding provider.'
143
143
  })
144
144
  }, 'LlmTextSearchPlugin adding text field to VectorNearbyInput');
@@ -151,7 +151,7 @@ export function createLlmTextSearchPlugin(options = {}) {
151
151
  },
152
152
  finalize(schema, build) {
153
153
  if (!build.llmEmbedder) {
154
- console.log('[graphile-llm] No embedder available — text on VectorNearbyInput is ignored ' +
154
+ console.log('[graphile-llm] No embedder available — text on VectorNearbyInput is rejected ' +
155
155
  'and unifiedSearch runs without pgvector. Configure an embedding provider to enable.');
156
156
  }
157
157
  return planTimeEmbedding(schema, build);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "graphile-llm",
3
- "version": "1.21.7",
3
+ "version": "1.21.8",
4
4
  "description": "LLM integration plugin for PostGraphile v5 — server-side text-to-vector embedding and text companion fields for pgvector columns",
5
5
  "author": "Constructive <developers@constructive.io>",
6
6
  "homepage": "https://github.com/constructive-io/constructive",
@@ -75,5 +75,5 @@
75
75
  "ollama",
76
76
  "openai"
77
77
  ],
78
- "gitHead": "ca40d826b329e282aafba0f48f90cf35aac816e0"
78
+ "gitHead": "723c0a02471517c3015a218dd179fa9f53b47980"
79
79
  }
@@ -10,15 +10,15 @@
10
10
  * accepts a compound input (point + distance) and the plugin handles
11
11
  * the conversion to SQL internally.
12
12
  *
13
- * Clients provide `text` or `vector`; `vector` becomes optional. When both
14
- * are given the vector wins. `unifiedSearch` text is embedded too, so pgvector
13
+ * Clients provide exactly one of `text` or `vector`; `vector` becomes optional.
14
+ * Sending both, or neither, is an error. `unifiedSearch` text is embedded too, so pgvector
15
15
  * participates alongside the text adapters.
16
16
  *
17
17
  * The text is embedded while the operation is planned (see embed-inputs.ts),
18
18
  * before SQL is built.
19
19
  *
20
- * If no embedder is configured, `text` is ignored and `unifiedSearch` runs
21
- * on the text adapters only.
20
+ * If no embedder is configured, `text` on `VectorNearbyInput` is an error and
21
+ * `unifiedSearch` runs on the text adapters only.
22
22
  *
23
23
  * If the embedder returns null (e.g. quota exceeded when the metering
24
24
  * plugin is loaded), behavior depends on `onQuotaExceeded`:
@@ -11,15 +11,15 @@
11
11
  * accepts a compound input (point + distance) and the plugin handles
12
12
  * the conversion to SQL internally.
13
13
  *
14
- * Clients provide `text` or `vector`; `vector` becomes optional. When both
15
- * are given the vector wins. `unifiedSearch` text is embedded too, so pgvector
14
+ * Clients provide exactly one of `text` or `vector`; `vector` becomes optional.
15
+ * Sending both, or neither, is an error. `unifiedSearch` text is embedded too, so pgvector
16
16
  * participates alongside the text adapters.
17
17
  *
18
18
  * The text is embedded while the operation is planned (see embed-inputs.ts),
19
19
  * before SQL is built.
20
20
  *
21
- * If no embedder is configured, `text` is ignored and `unifiedSearch` runs
22
- * on the text adapters only.
21
+ * If no embedder is configured, `text` on `VectorNearbyInput` is an error and
22
+ * `unifiedSearch` runs on the text adapters only.
23
23
  *
24
24
  * If the embedder returns null (e.g. quota exceeded when the metering
25
25
  * plugin is loaded), behavior depends on `onQuotaExceeded`:
@@ -142,7 +142,7 @@ function createLlmTextSearchPlugin(options = {}) {
142
142
  text: (0, embed_inputs_1.embedField)({ kind: 'nearby', into: 'vector', onQuotaExceeded }, {
143
143
  type: GraphQLString,
144
144
  description: 'Natural language text to embed server-side for similarity search. ' +
145
- 'Provide `text` or `vector`; if both are given, `vector` is used. ' +
145
+ 'Provide exactly one of `text` or `vector`; sending both or neither is an error. ' +
146
146
  'Requires the LLM plugin to be configured with an embedding provider.'
147
147
  })
148
148
  }, 'LlmTextSearchPlugin adding text field to VectorNearbyInput');
@@ -155,7 +155,7 @@ function createLlmTextSearchPlugin(options = {}) {
155
155
  },
156
156
  finalize(schema, build) {
157
157
  if (!build.llmEmbedder) {
158
- console.log('[graphile-llm] No embedder available — text on VectorNearbyInput is ignored ' +
158
+ console.log('[graphile-llm] No embedder available — text on VectorNearbyInput is rejected ' +
159
159
  'and unifiedSearch runs without pgvector. Configure an embedding provider to enable.');
160
160
  }
161
161
  return (0, embed_inputs_1.planTimeEmbedding)(schema, build);