Skip to content

Commit

Permalink
Merge pull request #92 from lgrammel/schema
Browse files Browse the repository at this point in the history
generateStructure
  • Loading branch information
lgrammel authored Sep 10, 2023
2 parents 6286ad3 + 61f0288 commit 6db542c
Show file tree
Hide file tree
Showing 75 changed files with 851 additions and 621 deletions.
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
# Changelog

## v0.30.0 - 2023-09-10

### Added

- You can now directly pass JSON schemas into `generateStructure` and `generateStructureOrText` calls without validation using `UncheckedJsonSchemaStructureDefinition`. This is useful when you need more flexility and don't require type inference. See `examples/basic/src/util/schema/generate-structure-unchecked-json-schema-example.ts`.

### Changed

- **BREAKING CHANGE**: renamed `generateJson` and `generateJsonOrText` to `generateStructure` and `generateStructureOrText`.
- **BREAKING CHANGE**: introduced `ZodSchema` and `ZodStructureDefinition`. These are required for `generateStructure` and `generateStructureOrText` calls and in tools.
- **BREAKING CHANGE**: renamed the corresponding methods and objects.

Why this breaking change?

ModelFusion is currently tied to Zod, but there are many other type checking libraries out there, and Zod does not map perfectly to JSON Schema (which is used in OpenAI function calling).
Enabling you to use JSON Schema directly in ModelFusion is a first step towards decoupling ModelFusion from Zod.
You can also configure your own schema adapters that e.g. use Ajv or another library.
Since this change already affected all JSON generation calls and tools, I included other changes that I had planned in the same area (e.g., renaming to generateStructure and making it more consistent).

## v0.29.0 - 2023-09-09

### Added
Expand Down
59 changes: 27 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,6 @@ ModelFusion is a library for building AI apps, chatbots, and agents. It provides
npm install modelfusion
```

You need to install `zod` and a matching version of `zod-to-json-schema` (peer dependencies):

```sh
npm install zod zod-to-json-schema
```

Or use a template: [ModelFusion terminal app starter](https://github.com/lgrammel/modelfusion-terminal-app-starter)

## Usage Examples
Expand Down Expand Up @@ -141,27 +135,27 @@ for (const choice of response.choices) {
console.log(`Duration: ${metadata.durationInMs}ms`);
```

### [Generate JSON](https://modelfusion.dev/guide/function/generate-json)
### [Generate Structure](https://modelfusion.dev/guide/function/generate-structure)

Generate JSON value that matches a schema.
Generate a structure that matches a schema.

```ts
const value = await generateJson(
const sentiment = await generateStructure(
new OpenAIChatModel({
model: "gpt-3.5-turbo",
temperature: 0,
maxCompletionTokens: 50,
}),
{
name: "sentiment" as const,
new ZodStructureDefinition({
name: "sentiment",
description: "Write the sentiment analysis",
schema: z.object({
sentiment: z
.enum(["positive", "neutral", "negative"])
.describe("Sentiment."),
}),
},
OpenAIChatFunctionPrompt.forSchemaCurried([
}),
OpenAIChatFunctionPrompt.forStructureCurried([
OpenAIChatMessage.system(
"You are a sentiment evaluator. " +
"Analyze the sentiment of the following product review:"
Expand All @@ -176,19 +170,16 @@ const value = await generateJson(

Providers: [OpenAI](https://modelfusion.dev/integration/model-provider/openai)

### [Generate JSON or Text](https://modelfusion.dev/guide/function/generate-json-or-text)
### [Generate Structure or Text](https://modelfusion.dev/guide/function/generate-structure-or-text)

Generate JSON (or text as a fallback) using a prompt and multiple schemas.
Generate a structure (or text as a fallback) using a prompt and multiple schemas.
It either matches one of the schemas or is text reponse.

```ts
const { schema, value, text } = await generateJsonOrText(
new OpenAIChatModel({
model: "gpt-3.5-turbo",
maxCompletionTokens: 1000,
}),
const { structure, value, text } = await generateStructureOrText(
new OpenAIChatModel({ model: "gpt-3.5-turbo", maxCompletionTokens: 1000 }),
[
{
new ZodStructureDefinition({
name: "getCurrentWeather" as const, // mark 'as const' for type inference
description: "Get the current weather in a given location",
schema: z.object({
Expand All @@ -197,16 +188,16 @@ const { schema, value, text } = await generateJsonOrText(
.describe("The city and state, e.g. San Francisco, CA"),
unit: z.enum(["celsius", "fahrenheit"]).optional(),
}),
},
{
}),
new ZodStructureDefinition({
name: "getContactInformation" as const,
description: "Get the contact information for a given person",
schema: z.object({
name: z.string().describe("The name of the person"),
}),
},
}),
],
OpenAIChatFunctionPrompt.forSchemasCurried([OpenAIChatMessage.user(query)])
OpenAIChatFunctionPrompt.forStructuresCurried([OpenAIChatMessage.user(query)])
);
```

Expand All @@ -227,11 +218,15 @@ const calculator = new Tool({
name: "calculator",
description: "Execute a calculation",

inputSchema: z.object({
a: z.number().describe("The first number."),
b: z.number().describe("The second number."),
operator: z.enum(["+", "-", "*", "/"]).describe("The operator."),
}),
inputSchema: new ZodSchema(
z.object({
a: z.number().describe("The first number."),
b: z.number().describe("The second number."),
operator: z
.enum(["+", "-", "*", "/"])
.describe("The operator (+, -, *, /)."),
})
),

execute: async ({ a, b, operator }) => {
switch (operator) {
Expand Down Expand Up @@ -421,8 +416,8 @@ Integrations: [Helicone](https://modelfusion.dev/integration/observability/helic

- [Model Functions](https://modelfusion.dev/guide/function/)
- [Generate and stream text](https://modelfusion.dev/guide/function/generate-text)
- [Generate JSON](https://modelfusion.dev/guide/function/generate-json)
- [Generate JSON or text](https://modelfusion.dev/guide/function/generate-json-or-text)
- [Generate structure](https://modelfusion.dev/guide/function/generate-structure)
- [Generate structure or text](https://modelfusion.dev/guide/function/generate-structure-or-text)
- [Embed Text](https://modelfusion.dev/guide/function/embed-text)
- [Tokenize Text](https://modelfusion.dev/guide/function/tokenize-text)
- [Transcribe Speech](https://modelfusion.dev/guide/function/transcribe-speech)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,25 @@
sidebar_position: 6
---

# Generate JSON or Text
# Generate Structure or Text

Generates JSON (or text as a fallback) using a prompt and multiple schemas.
Generates a structure (or text as a fallback) using a prompt and multiple schemas.
It either matches one of the schemas or is text reponse.
This is useful for building chatbots with plugins and agents.

## Usage

### generateJsonOrText
### generateStructureOrText

[generateJsonOrText API](/api/modules#generatejsonortext)
[generateStructureOrText API](/api/modules#generatestructureortext)

#### OpenAI chat model

```ts
const { schema, value, text } = await generateJsonOrText(
new OpenAIChatModel({
model: "gpt-3.5-turbo",
maxCompletionTokens: 1000,
}),
const { structure, value, text } = await generateStructureOrText(
new OpenAIChatModel({ model: "gpt-3.5-turbo", maxCompletionTokens: 1000 }),
[
{
new ZodStructureDefinition({
name: "getCurrentWeather" as const, // mark 'as const' for type inference
description: "Get the current weather in a given location",
schema: z.object({
Expand All @@ -32,27 +29,27 @@ const { schema, value, text } = await generateJsonOrText(
.describe("The city and state, e.g. San Francisco, CA"),
unit: z.enum(["celsius", "fahrenheit"]).optional(),
}),
},
{
}),
new ZodStructureDefinition({
name: "getContactInformation" as const,
description: "Get the contact information for a given person",
schema: z.object({
name: z.string().describe("The name of the person"),
}),
},
}),
],
OpenAIChatFunctionPrompt.forSchemasCurried([OpenAIChatMessage.user(query)])
OpenAIChatFunctionPrompt.forStructuresCurried([OpenAIChatMessage.user(query)])
);
```

The result contains:

- `schema`: The name of the schema that was matched or `null` if text was generated.
- `value`: The value of the schema that was matched or `null` if text was generated.
- `text`: The generated text. Optional when a schema was matched.
- `structure`: The name of the structure that was matched or `null` if text was generated.
- `value`: The value of the structure that was matched or `null` if text was generated.
- `text`: The generated text. Optional when a structure was matched.

```ts
switch (schema) {
switch (structure) {
case "getCurrentWeather": {
const { location, unit } = value;
console.log("getCurrentWeather", location, unit);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,37 @@
sidebar_position: 5
---

# Generate JSON
# Generate Structure

Generates JSON that matches a schema.
Generates a structure that matches a schema.
This is, for example, useful for [information extraction](/tutorial/recipes/information-extraction)
and classification tasks (e.g. [sentiment analysis](/tutorial/recipes/sentiment-analysis)).

## Usage

### generateJson
### generateStructure

[generateJson API](/api/modules#generatejson)
[generateStructure API](/api/modules#generatestructure)

#### OpenAI chat model

```ts
const sentiment = await generateJson(
const sentiment = await generateStructure(
new OpenAIChatModel({
model: "gpt-3.5-turbo",
temperature: 0,
maxCompletionTokens: 50,
}),
{
name: "sentiment" as const,
new ZodStructureDefinition({
name: "sentiment",
description: "Write the sentiment analysis",
schema: z.object({
sentiment: z
.enum(["positive", "neutral", "negative"])
.describe("Sentiment."),
}),
},
OpenAIChatFunctionPrompt.forSchemaCurried([
}),
OpenAIChatFunctionPrompt.forStructureCurried([
OpenAIChatMessage.system(
"You are a sentiment evaluator. " +
"Analyze the sentiment of the following product review:"
Expand Down
6 changes: 0 additions & 6 deletions docs/guide/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,6 @@ ModelFusion is in its initial development phase. Until version 1.0 there may be
npm install modelfusion
```

You need to install `zod` and a matching version of `zod-to-json-schema` (peer dependencies):

```sh
npm install zod zod-to-json-schema
```

Or use a template: [ModelFusion terminal app starter](https://github.com/lgrammel/modelfusion-terminal-app-starter)

## API Keys
Expand Down
25 changes: 17 additions & 8 deletions docs/guide/tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,15 @@ const calculator = new Tool({
name: "calculator",
description: "Execute a calculation",

inputSchema: z.object({
a: z.number().describe("The first number."),
b: z.number().describe("The second number."),
operator: z.enum(["+", "-", "*", "/"]).describe("The operator."),
}),

inputSchema: new ZodSchema(
z.object({
a: z.number().describe("The first number."),
b: z.number().describe("The second number."),
operator: z
.enum(["+", "-", "*", "/"])
.describe("The operator (+, -, *, /)."),
})
),
execute: async ({ a, b, operator }) => {
switch (operator) {
case "+":
Expand All @@ -55,7 +58,7 @@ const calculator = new Tool({

[useTool API](/api/modules/#useTool)

`useTool` uses [generateJson](/guide/function/generate-json) to generate parameters for a tool and then executes the tool with the parameters.
`useTool` uses [generateStructure](/guide/function/generate-structure) to generate parameters for a tool and then executes the tool with the parameters.

The result contains the name of the tool (`tool` property), the parameters (`parameters` property, typed), and the result of the tool execution (`result` property, typed).

Expand All @@ -73,7 +76,7 @@ const { tool, parameters, result } = await useTool(

[useToolOrGenerateText API](/api/modules/#useToolorgeneratetext)

`useToolOrGenerateText` uses [generateJsonOrText](/guide/function/generate-json-or-text)
`useToolOrGenerateText` uses [generateStructureOrText](/guide/function/generate-structure-or-text)
to select a tool, generate parameters for it and execute it.
It can be configured with several tools.

Expand Down Expand Up @@ -170,3 +173,9 @@ The following tools are available as a separate packages:
> _terminal app_, _agent_, _tools_, _GPT-4_
Small agent that solves middle school math problems. It uses a calculator tool to solve the problems.

### [Wikipedia Agent](https://github.com/lgrammel/modelfusion/tree/main/examples/wikipedia-agent)

> _terminal app_, _ReAct agent_, _GPT-4_, _OpenAI functions_, _tools_
Get answers to questions from Wikipedia, e.g. "Who was born first, Einstein or Picasso?"
2 changes: 1 addition & 1 deletion docs/integration/model-provider/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Model providers (e.g., OpenAI) offer APIs (either local or in the cloud) for usi
| [Stream text](/guide/function/generate-text) |||| |
| [Tokenize text](/guide/function/tokenize-text) | full | full | basic | |

### [Generate JSON](/guide/function/generate-json) and [Generate JSON or Text](/guide/function/generate-json-or-text)
### [Generate Structure](/guide/function/generate-structure) and [Generate Structure or Text](/guide/function/generate-structure-or-text)

- [OpenAI](/integration/model-provider/openai) chat models

Expand Down
Loading

1 comment on commit 6db542c

@vercel
Copy link

@vercel vercel bot commented on 6db542c Sep 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.