diff --git a/.vitepress/config.ts b/.vitepress/config.ts index 21f04f9a..c6e3c26e 100644 --- a/.vitepress/config.ts +++ b/.vitepress/config.ts @@ -181,13 +181,20 @@ function getApiReferenceSidebar(): typeof typedocSidebar { } function orderApiReferenceSidebar(sidebar: typeof typedocSidebar): typeof typedocSidebar { + orderClasses(sidebar); + orderTypes(sidebar); + + return sidebar; +} + +function orderClasses(sidebar: typeof typedocSidebar) { const baseChatPromptWrapper = "ChatPromptWrapper"; const chatPromptWrapperItems: DefaultTheme.SidebarItem[] = []; const classes = sidebar.find((item) => item.text === "Classes"); if (classes == null || !(classes.items instanceof Array)) - return sidebar; + return; (classes.items as DefaultTheme.SidebarItem[]).unshift({ text: "Chat wrappers", @@ -222,7 +229,54 @@ function orderApiReferenceSidebar(sidebar: typeof typedocSidebar): typeof typedo return aIndex - bIndex; }); - - return sidebar; } +function orderTypes(sidebar: typeof typedocSidebar) { + const types = sidebar.find((item) => item.text === "Types"); + + if (types == null || !(types.items instanceof Array)) + return; + + function groupGbnfJsonSchema() { + if (types == null || !(types.items instanceof Array)) + return; + + const gbnfJsonSchemaItemTitle = "GbnfJsonSchema"; + const gbnfItemsPrefix = "GbnfJson"; + const gbnfJsonSchemaItems: DefaultTheme.SidebarItem[] = []; + + const gbnfJsonSchemaItem = types.items + .find((item) => item.text === gbnfJsonSchemaItemTitle) as DefaultTheme.SidebarItem | null; + + if (gbnfJsonSchemaItem == null) + return; + + gbnfJsonSchemaItem.collapsed = true; + gbnfJsonSchemaItem.items = gbnfJsonSchemaItems; + + for (const item of types.items.slice()) { + if (item.text === gbnfJsonSchemaItemTitle || !item.text.startsWith(gbnfItemsPrefix)) + continue; + + types.items.splice(types.items.indexOf(item), 1); + gbnfJsonSchemaItems.push(item); + } + } + + function moveCollapseItemsToTheEnd() { + if (types == null || !(types.items instanceof Array)) + return; + + types.items.sort((a, b) => { + if (a.collapsed && !b.collapsed) + return 1; + if (!a.collapsed && b.collapsed) + return -1; + + return 0; + }); + } + + groupGbnfJsonSchema(); + moveCollapseItemsToTheEnd(); +} diff --git a/README.md b/README.md index 0e3d1905..fa4e4422 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ * Chat with a model using a chat wrapper * Use the CLI to chat with a model without writing any code * Up-to-date with the latest version of `llama.cpp`. Download and compile the latest release with a single CLI command. -* Force a model to generate output in a parseable format, like JSON +* Force a model to generate output in a parseable format, like JSON, or even force it to follow a specific JSON schema ## [Documentation](https://withcatai.github.io/node-llama-cpp/) * [Getting started guide](https://withcatai.github.io/node-llama-cpp/guide/) diff --git a/docs/guide/grammar.md b/docs/guide/grammar.md index 4eeb87c8..c85864bd 100644 --- a/docs/guide/grammar.md +++ b/docs/guide/grammar.md @@ -57,6 +57,50 @@ console.log("AI: " + a2); console.log(JSON.parse(a2)); ``` +## Using a JSON schema grammar +The [`LlamaGrammar.getForJsonSchema()`](/api/classes/LlamaGrammar#getforjsonschema) creates a GBNF grammar based on the [JSON schema](https://json-schema.org/learn/getting-started-step-by-step) you provide, and then returns a [`LlamaGrammar`](/api/classes/LlamaGrammar). + +It only supports a small subset of the JSON schema spec, but it's enough to generate useful JSON objects using a text generation model. + +To see what subset of the JSON schema spec is supported, see the [`GbnfJsonSchema` type](/api/type-aliases/GbnfJsonSchema). + +```typescript +import {fileURLToPath} from "url"; +import path from "path"; +import {LlamaModel, LlamaJsonSchemaGrammar, LlamaContext, LlamaChatSession} from "node-llama-cpp"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +const model = new LlamaModel({ + modelPath: path.join(__dirname, "models", "codellama-13b.Q3_K_M.gguf") +}) +const grammar = new LlamaJsonSchemaGrammar({ + "type": "object", + "properties": { + "responseMessage": { + "type": "string" + }, + "requestPositivityScoreFromOneToTen": { + "type": "number" + } + } +} as const); +const context = new LlamaContext({ + model +}); +const session = new LlamaChatSession({context}); + + +const q1 = 'How are you doing?'; +console.log("User: " + q1); + +const a1 = await session.prompt(q1, {grammar, maxTokens: context.getContextSize()}); +console.log("AI: " + a1); + +const parsedA1 = grammar.parse(a1); +console.log(parsedA1.responseMessage, parsedA1.requestPositivityScoreFromOneToTen); +``` + ## Creating your own grammar To create your own grammar, read the [GBNF guide](https://github.com/ggerganov/llama.cpp/blob/f5fe98d11bdf9e7797bcfb05c0c3601ffc4b9d26/grammars/README.md) to create a GBNF grammar file. diff --git a/docs/index.md b/docs/index.md index f982bee3..4cf9ba07 100644 --- a/docs/index.md +++ b/docs/index.md @@ -36,7 +36,7 @@ features: linkText: Learn more - icon: title: Output format - details: Force a model to generate output in a parseable format, like JSON + details: Force a model to generate output in a parseable format, like JSON, or even force it to follow a specific JSON schema link: /guide/grammar linkText: Learn more ---