@nixxie-cms/core 1.1.0 → 2.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/CHANGELOG.md +36 -0
- package/context/dist/nixxie-cms-core-context.cjs.js +1 -1
- package/context/dist/nixxie-cms-core-context.esm.js +1 -1
- package/dist/declarations/src/schema.d.ts.map +1 -1
- package/dist/declarations/src/types/config/index.d.ts +20 -1
- package/dist/declarations/src/types/config/index.d.ts.map +1 -1
- package/dist/declarations/src/types/context.d.ts +158 -0
- package/dist/declarations/src/types/context.d.ts.map +1 -1
- package/dist/{express-7ca6f76a.cjs.js → express-84d534c2.cjs.js} +1 -1
- package/dist/{express-0abbce07.esm.js → express-d0a4ce99.esm.js} +1 -1
- package/dist/nixxie-cms-core.cjs.js +1 -0
- package/dist/nixxie-cms-core.esm.js +1 -0
- package/dist/{system-69e1a285.cjs.js → system-6b37a5f8.cjs.js} +2 -1
- package/dist/{system-4d2a2648.esm.js → system-e591d821.esm.js} +2 -1
- package/internal-unstable/artifacts/dist/nixxie-cms-core-internal-unstable-artifacts.cjs.js +2 -2
- package/internal-unstable/artifacts/dist/nixxie-cms-core-internal-unstable-artifacts.esm.js +2 -2
- package/package.json +2 -2
- package/scripts/cli/dist/nixxie-cms-core-scripts-cli.cjs.js +2 -2
- package/scripts/cli/dist/nixxie-cms-core-scripts-cli.esm.js +2 -2
- package/scripts/dist/nixxie-cms-core-scripts.cjs.js +2 -2
- package/scripts/dist/nixxie-cms-core-scripts.esm.js +2 -2
- package/src/fields/types/timestamp/views/__tests__/index.tsx +68 -68
- package/src/lib/context/createContext.ts +1 -0
- package/src/schema.ts +1 -0
- package/src/types/config/index.ts +21 -0
- package/src/types/context.ts +166 -0
- package/tests/conditional-filters.test.ts +333 -326
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { cli } from '../cli/dist/nixxie-cms-core-scripts-cli.esm.js';
|
|
2
|
-
import { E as ExitError } from '../../dist/express-
|
|
2
|
+
import { E as ExitError } from '../../dist/express-d0a4ce99.esm.js';
|
|
3
3
|
import 'meow';
|
|
4
4
|
import 'esbuild';
|
|
5
5
|
import '@nodelib/fs.walk';
|
|
@@ -8,7 +8,7 @@ import 'node:fs/promises';
|
|
|
8
8
|
import 'node:path';
|
|
9
9
|
import 'node:util';
|
|
10
10
|
import 'resolve';
|
|
11
|
-
import '../../dist/system-
|
|
11
|
+
import '../../dist/system-e591d821.esm.js';
|
|
12
12
|
import 'node:crypto';
|
|
13
13
|
import '../../dist/admin-meta-14c60fec.esm.js';
|
|
14
14
|
import 'graphql';
|
|
@@ -1,68 +1,68 @@
|
|
|
1
|
-
import { controller } from '../index'
|
|
2
|
-
|
|
3
|
-
const STUBCONFIG = {
|
|
4
|
-
listKey: 'timestamp',
|
|
5
|
-
fieldKey: 'timestamp',
|
|
6
|
-
label: 'foo',
|
|
7
|
-
description: '',
|
|
8
|
-
customViews: {},
|
|
9
|
-
} as const
|
|
10
|
-
|
|
11
|
-
describe('controller', () => {
|
|
12
|
-
describe('validate', () => {
|
|
13
|
-
it('null is OK if not required', () => {
|
|
14
|
-
const { validate } = controller({
|
|
15
|
-
...STUBCONFIG,
|
|
16
|
-
fieldMeta: {
|
|
17
|
-
defaultValue: null,
|
|
18
|
-
updatedAt: false,
|
|
19
|
-
},
|
|
20
|
-
})
|
|
21
|
-
expect(
|
|
22
|
-
validate!(
|
|
23
|
-
{
|
|
24
|
-
kind: 'create',
|
|
25
|
-
value: null,
|
|
26
|
-
},
|
|
27
|
-
{ isRequired: false }
|
|
28
|
-
)
|
|
29
|
-
).toBe(true)
|
|
30
|
-
})
|
|
31
|
-
it('isRequired enforces required (null)', () => {
|
|
32
|
-
const { validate } = controller({
|
|
33
|
-
...STUBCONFIG,
|
|
34
|
-
fieldMeta: {
|
|
35
|
-
defaultValue: null,
|
|
36
|
-
updatedAt: false,
|
|
37
|
-
},
|
|
38
|
-
})
|
|
39
|
-
expect(
|
|
40
|
-
validate!(
|
|
41
|
-
{
|
|
42
|
-
kind: 'create',
|
|
43
|
-
value: null,
|
|
44
|
-
},
|
|
45
|
-
{ isRequired: true }
|
|
46
|
-
)
|
|
47
|
-
).toBe(
|
|
48
|
-
})
|
|
49
|
-
it('isRequired enforces required (value)', () => {
|
|
50
|
-
const { validate } = controller({
|
|
51
|
-
...STUBCONFIG,
|
|
52
|
-
fieldMeta: {
|
|
53
|
-
defaultValue: null,
|
|
54
|
-
updatedAt: false,
|
|
55
|
-
},
|
|
56
|
-
})
|
|
57
|
-
expect(
|
|
58
|
-
validate!(
|
|
59
|
-
{
|
|
60
|
-
kind: 'create',
|
|
61
|
-
value: new Date().toJSON(),
|
|
62
|
-
},
|
|
63
|
-
{ isRequired: true }
|
|
64
|
-
)
|
|
65
|
-
).toBe(true)
|
|
66
|
-
})
|
|
67
|
-
})
|
|
68
|
-
})
|
|
1
|
+
import { controller } from '../index'
|
|
2
|
+
|
|
3
|
+
const STUBCONFIG = {
|
|
4
|
+
listKey: 'timestamp',
|
|
5
|
+
fieldKey: 'timestamp',
|
|
6
|
+
label: 'foo',
|
|
7
|
+
description: '',
|
|
8
|
+
customViews: {},
|
|
9
|
+
} as const
|
|
10
|
+
|
|
11
|
+
describe('controller', () => {
|
|
12
|
+
describe('validate', () => {
|
|
13
|
+
it('null is OK if not required', () => {
|
|
14
|
+
const { validate } = controller({
|
|
15
|
+
...STUBCONFIG,
|
|
16
|
+
fieldMeta: {
|
|
17
|
+
defaultValue: null,
|
|
18
|
+
updatedAt: false,
|
|
19
|
+
},
|
|
20
|
+
})
|
|
21
|
+
expect(
|
|
22
|
+
validate!(
|
|
23
|
+
{
|
|
24
|
+
kind: 'create',
|
|
25
|
+
value: null,
|
|
26
|
+
},
|
|
27
|
+
{ isRequired: false }
|
|
28
|
+
)
|
|
29
|
+
).toBe(true)
|
|
30
|
+
})
|
|
31
|
+
it('isRequired enforces required (null)', () => {
|
|
32
|
+
const { validate } = controller({
|
|
33
|
+
...STUBCONFIG,
|
|
34
|
+
fieldMeta: {
|
|
35
|
+
defaultValue: null,
|
|
36
|
+
updatedAt: false,
|
|
37
|
+
},
|
|
38
|
+
})
|
|
39
|
+
expect(
|
|
40
|
+
validate!(
|
|
41
|
+
{
|
|
42
|
+
kind: 'create',
|
|
43
|
+
value: null,
|
|
44
|
+
},
|
|
45
|
+
{ isRequired: true }
|
|
46
|
+
)
|
|
47
|
+
).toBe(false)
|
|
48
|
+
})
|
|
49
|
+
it('isRequired enforces required (value)', () => {
|
|
50
|
+
const { validate } = controller({
|
|
51
|
+
...STUBCONFIG,
|
|
52
|
+
fieldMeta: {
|
|
53
|
+
defaultValue: null,
|
|
54
|
+
updatedAt: false,
|
|
55
|
+
},
|
|
56
|
+
})
|
|
57
|
+
expect(
|
|
58
|
+
validate!(
|
|
59
|
+
{
|
|
60
|
+
kind: 'create',
|
|
61
|
+
value: new Date().toJSON(),
|
|
62
|
+
},
|
|
63
|
+
{ isRequired: true }
|
|
64
|
+
)
|
|
65
|
+
).toBe(true)
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
})
|
|
@@ -93,6 +93,7 @@ export function createContext({
|
|
|
93
93
|
search: config.search ?? null,
|
|
94
94
|
notifications: config.notifications ?? null,
|
|
95
95
|
ai: config.ai ?? null,
|
|
96
|
+
aiRag: config.aiRag ?? null,
|
|
96
97
|
versioning: config.versioning ?? null,
|
|
97
98
|
workflow: config.workflow ?? null,
|
|
98
99
|
apiKeys: config.apiKeys ?? null,
|
package/src/schema.ts
CHANGED
|
@@ -220,6 +220,7 @@ export function buildConfig<TypeInfo extends BaseNixxieTypeInfo>(
|
|
|
220
220
|
search: config.search,
|
|
221
221
|
notifications: config.notifications,
|
|
222
222
|
ai: config.ai,
|
|
223
|
+
aiRag: config.aiRag,
|
|
223
224
|
versioning: config.versioning,
|
|
224
225
|
workflow: config.workflow,
|
|
225
226
|
apiKeys: config.apiKeys,
|
|
@@ -10,6 +10,7 @@ import type { GraphQLSchema } from 'graphql'
|
|
|
10
10
|
import type {
|
|
11
11
|
BaseNixxieTypeInfo,
|
|
12
12
|
DatabaseProvider,
|
|
13
|
+
NixxieAiRagService,
|
|
13
14
|
NixxieAiService,
|
|
14
15
|
NixxieApiKeysService,
|
|
15
16
|
NixxieAuditService,
|
|
@@ -420,6 +421,25 @@ export type NixxieConfigPre<TypeInfo extends BaseNixxieTypeInfo = BaseNixxieType
|
|
|
420
421
|
*/
|
|
421
422
|
ai?: NixxieAiService
|
|
422
423
|
|
|
424
|
+
/**
|
|
425
|
+
* Custom RAG assistant created with `createAiRag()` from `@nixxie-cms/ai-rag`.
|
|
426
|
+
* Available as `context.services.aiRag`. Stores a knowledge base, indexes it into
|
|
427
|
+
* embeddings, and answers questions grounded in that content with citations and
|
|
428
|
+
* hallucination guarding. Prefer registering it via `ragPlugin()` so the knowledge-base
|
|
429
|
+
* collections, HTTP routes and scheduled indexing are wired automatically.
|
|
430
|
+
*
|
|
431
|
+
* @example
|
|
432
|
+
* ```ts
|
|
433
|
+
* import { createAiRag, ragPlugin } from '@nixxie-cms/ai-rag'
|
|
434
|
+
* const aiRag = createAiRag({
|
|
435
|
+
* generation: { provider: 'anthropic', apiKey: process.env.ANTHROPIC_API_KEY! },
|
|
436
|
+
* embedding: { provider: 'openai', apiKey: process.env.OPENAI_API_KEY! },
|
|
437
|
+
* })
|
|
438
|
+
* export default config({ aiRag, plugins: [ragPlugin({ service: aiRag })] })
|
|
439
|
+
* ```
|
|
440
|
+
*/
|
|
441
|
+
aiRag?: NixxieAiRagService
|
|
442
|
+
|
|
423
443
|
/**
|
|
424
444
|
* Content revision history created with `createVersioning()` from
|
|
425
445
|
* `@nixxie-cms/versioning`. Available as `context.services.versioning`.
|
|
@@ -560,6 +580,7 @@ export type NixxieConfig<TypeInfo extends BaseNixxieTypeInfo = BaseNixxieTypeInf
|
|
|
560
580
|
search: NixxieSearchService | undefined
|
|
561
581
|
notifications: NixxieNotificationsService | undefined
|
|
562
582
|
ai: NixxieAiService | undefined
|
|
583
|
+
aiRag: NixxieAiRagService | undefined
|
|
563
584
|
versioning: NixxieVersioningService | undefined
|
|
564
585
|
workflow: NixxieWorkflowService | undefined
|
|
565
586
|
apiKeys: NixxieApiKeysService | undefined
|
package/src/types/context.ts
CHANGED
|
@@ -562,6 +562,171 @@ export type NixxieAiService = {
|
|
|
562
562
|
embedMany(texts: string[]): Promise<number[][]>
|
|
563
563
|
}
|
|
564
564
|
|
|
565
|
+
// ── AI RAG service (retrieval-augmented knowledge base) ──
|
|
566
|
+
|
|
567
|
+
/** A source document stored in the knowledge base that the assistant is trained on. */
|
|
568
|
+
export type NixxieRagDocumentInput = {
|
|
569
|
+
/** Short title/label for the document. */
|
|
570
|
+
title?: string
|
|
571
|
+
/** The body the assistant learns from. */
|
|
572
|
+
content: string
|
|
573
|
+
/** Origin of the content (URL, filename, list:itemId, …) — shown in citations. */
|
|
574
|
+
source?: string
|
|
575
|
+
/** Free-form tags used to scope retrieval (e.g. ['billing', 'public']). */
|
|
576
|
+
tags?: string[]
|
|
577
|
+
/** Arbitrary structured metadata returned alongside citations. */
|
|
578
|
+
metadata?: Record<string, unknown>
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
export type NixxieRagDocument = NixxieRagDocumentInput & {
|
|
582
|
+
id: string
|
|
583
|
+
/** Indexing lifecycle of the document's chunks. */
|
|
584
|
+
status: 'pending' | 'indexing' | 'indexed' | 'error' | 'disabled'
|
|
585
|
+
/** Number of chunks currently indexed for this document. */
|
|
586
|
+
chunkCount: number
|
|
587
|
+
/** Last error encountered while indexing, if any. */
|
|
588
|
+
error?: string
|
|
589
|
+
createdAt: Date
|
|
590
|
+
updatedAt: Date
|
|
591
|
+
indexedAt?: Date
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
export type NixxieRagDocumentQuery = {
|
|
595
|
+
take?: number
|
|
596
|
+
skip?: number
|
|
597
|
+
/** Only return documents matching ALL of these tags. */
|
|
598
|
+
tags?: string[]
|
|
599
|
+
status?: NixxieRagDocument['status']
|
|
600
|
+
/** Substring match against title/content/source. */
|
|
601
|
+
search?: string
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
/** A retrieved chunk with its similarity score, used to ground answers. */
|
|
605
|
+
export type NixxieRagChunk = {
|
|
606
|
+
id: string
|
|
607
|
+
documentId: string
|
|
608
|
+
title?: string
|
|
609
|
+
source?: string
|
|
610
|
+
/** The chunk text. */
|
|
611
|
+
content: string
|
|
612
|
+
/** Cosine similarity to the query in [0, 1] (higher is more relevant). */
|
|
613
|
+
score: number
|
|
614
|
+
tags?: string[]
|
|
615
|
+
metadata?: Record<string, unknown>
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
export type NixxieRagRetrieveOptions = {
|
|
619
|
+
/** Number of chunks to return. Defaults to the configured `retrieval.topK`. */
|
|
620
|
+
topK?: number
|
|
621
|
+
/** Drop chunks scoring below this threshold. Defaults to `retrieval.minScore`. */
|
|
622
|
+
minScore?: number
|
|
623
|
+
/** Restrict retrieval to documents carrying ALL of these tags. */
|
|
624
|
+
tags?: string[]
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
/** A single source the answer was grounded on. */
|
|
628
|
+
export type NixxieRagCitation = {
|
|
629
|
+
documentId: string
|
|
630
|
+
chunkId: string
|
|
631
|
+
title?: string
|
|
632
|
+
source?: string
|
|
633
|
+
score: number
|
|
634
|
+
/** Short excerpt of the cited chunk. */
|
|
635
|
+
snippet: string
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
export type NixxieRagAskOptions = NixxieRagRetrieveOptions & {
|
|
639
|
+
/** Override the generation model for this call. */
|
|
640
|
+
model?: string
|
|
641
|
+
/** Override sampling temperature for this call. */
|
|
642
|
+
temperature?: number
|
|
643
|
+
/** Override max output tokens for this call. */
|
|
644
|
+
maxTokens?: number
|
|
645
|
+
/** Append an extra instruction to the system prompt for this call. */
|
|
646
|
+
systemSuffix?: string
|
|
647
|
+
/** Pre-retrieved chunks to use instead of running retrieval (advanced). */
|
|
648
|
+
context?: NixxieRagChunk[]
|
|
649
|
+
/** Per-call override of the hallucination guard. */
|
|
650
|
+
guard?: boolean
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
/** The grounded answer returned by `ask`/`chat`. */
|
|
654
|
+
export type NixxieRagAnswer = {
|
|
655
|
+
/** The assistant's answer text. */
|
|
656
|
+
text: string
|
|
657
|
+
/** The sources the answer was grounded on. */
|
|
658
|
+
sources: NixxieRagCitation[]
|
|
659
|
+
/** Whether the answer passed the grounding/hallucination check. */
|
|
660
|
+
grounded: boolean
|
|
661
|
+
/** True when the assistant refused because no relevant context was found. */
|
|
662
|
+
refused: boolean
|
|
663
|
+
model: string
|
|
664
|
+
usage?: { inputTokens?: number; outputTokens?: number }
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/** A streamed token or lifecycle event from `stream`. */
|
|
668
|
+
export type NixxieRagStreamEvent =
|
|
669
|
+
| { type: 'sources'; sources: NixxieRagCitation[] }
|
|
670
|
+
| { type: 'token'; token: string }
|
|
671
|
+
| { type: 'done'; answer: NixxieRagAnswer }
|
|
672
|
+
| { type: 'error'; error: string }
|
|
673
|
+
|
|
674
|
+
export type NixxieRagIndexStats = {
|
|
675
|
+
/** Documents processed this run. */
|
|
676
|
+
documents: number
|
|
677
|
+
/** Chunks (re)embedded this run. */
|
|
678
|
+
chunks: number
|
|
679
|
+
/** Documents that failed to index. */
|
|
680
|
+
errors: number
|
|
681
|
+
/** Wall-clock duration in milliseconds. */
|
|
682
|
+
durationMs: number
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
* A custom, RAG-trained assistant backed by a knowledge-base collection.
|
|
687
|
+
* Implemented by @nixxie-cms/ai-rag. Add/remove/retrieve KB content, (re)index it
|
|
688
|
+
* into embeddings, and ask grounded, citation-backed questions with hallucination guarding.
|
|
689
|
+
*/
|
|
690
|
+
export type NixxieAiRagService = NixxieInitableService & {
|
|
691
|
+
// ── Knowledge-base content ──
|
|
692
|
+
/** Add a document to the knowledge base (queued for indexing). */
|
|
693
|
+
addDocument(doc: NixxieRagDocumentInput): Promise<NixxieRagDocument>
|
|
694
|
+
/** Add many documents in one call. */
|
|
695
|
+
addDocuments(docs: NixxieRagDocumentInput[]): Promise<NixxieRagDocument[]>
|
|
696
|
+
/** Fetch a single document by id. */
|
|
697
|
+
getDocument(id: string): Promise<NixxieRagDocument | undefined>
|
|
698
|
+
/** List documents in the knowledge base. */
|
|
699
|
+
listDocuments(query?: NixxieRagDocumentQuery): Promise<NixxieRagDocument[]>
|
|
700
|
+
/** Patch a document's content/metadata (re-queues it for indexing). */
|
|
701
|
+
updateDocument(id: string, patch: Partial<NixxieRagDocumentInput>): Promise<NixxieRagDocument>
|
|
702
|
+
/** Remove a document and all of its indexed chunks. */
|
|
703
|
+
removeDocument(id: string): Promise<void>
|
|
704
|
+
|
|
705
|
+
// ── Indexing ──
|
|
706
|
+
/** (Re)index a single document now. */
|
|
707
|
+
index(documentId: string): Promise<void>
|
|
708
|
+
/** (Re)index documents. Pass `{ force: true }` to rebuild already-indexed docs. */
|
|
709
|
+
reindex(options?: { force?: boolean; tags?: string[] }): Promise<NixxieRagIndexStats>
|
|
710
|
+
/** Index every document still pending/changed. */
|
|
711
|
+
indexPending(): Promise<NixxieRagIndexStats>
|
|
712
|
+
|
|
713
|
+
// ── Retrieval & chat ──
|
|
714
|
+
/** Return the most relevant chunks for a query (no generation). */
|
|
715
|
+
retrieve(query: string, options?: NixxieRagRetrieveOptions): Promise<NixxieRagChunk[]>
|
|
716
|
+
/** Ask a one-shot question and get a grounded, cited answer. */
|
|
717
|
+
ask(question: string, options?: NixxieRagAskOptions): Promise<NixxieRagAnswer>
|
|
718
|
+
/** Multi-turn chat; the latest user turn drives retrieval. */
|
|
719
|
+
chat(messages: NixxieAiMessage[], options?: NixxieRagAskOptions): Promise<NixxieRagAnswer>
|
|
720
|
+
/** Streamed variant of `chat`, yielding sources, then tokens, then the final answer. */
|
|
721
|
+
stream(
|
|
722
|
+
messages: NixxieAiMessage[],
|
|
723
|
+
options?: NixxieRagAskOptions
|
|
724
|
+
): AsyncIterable<NixxieRagStreamEvent>
|
|
725
|
+
|
|
726
|
+
/** Gracefully stop background indexing and flush pending work. */
|
|
727
|
+
close(): Promise<void>
|
|
728
|
+
}
|
|
729
|
+
|
|
565
730
|
// ── Versioning service ──
|
|
566
731
|
|
|
567
732
|
export type NixxieVersionEntry = {
|
|
@@ -912,6 +1077,7 @@ export type NixxieContext<TypeInfo extends BaseNixxieTypeInfo = BaseNixxieTypeIn
|
|
|
912
1077
|
search: NixxieSearchService | null
|
|
913
1078
|
notifications: NixxieNotificationsService | null
|
|
914
1079
|
ai: NixxieAiService | null
|
|
1080
|
+
aiRag: NixxieAiRagService | null
|
|
915
1081
|
versioning: NixxieVersioningService | null
|
|
916
1082
|
workflow: NixxieWorkflowService | null
|
|
917
1083
|
apiKeys: NixxieApiKeysService | null
|