Skip to content

Commit 394737b

Browse files
Update AssemblyScript SDK and examples
1 parent a0ae35d commit 394737b

File tree

13 files changed

+1957
-836
lines changed

13 files changed

+1957
-836
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
- chore: remove localHypermodeModels list and handle 404s properly instead in local dev
1010
[#703](https://github.com/hypermodeinc/modus/pull/703)
1111
- fix: remove fallback to default time zone [#706](https://github.com/hypermodeinc/modus/pull/706)
12+
- feat: improve OpenAI model APIs and examples to better support audio, images, and tool calling
13+
[#707](https://github.com/hypermodeinc/modus/pull/707)
1214

1315
## 2025-01-09 - CLI 0.16.6
1416

sdk/assemblyscript/examples/textgeneration/assembly/index.ts

Lines changed: 6 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -4,109 +4,10 @@
44
* See the LICENSE file that accompanied this code for further details.
55
*/
66

7-
import { JSON } from "json-as";
8-
import { models } from "@hypermode/modus-sdk-as";
9-
import { Product, sampleProductJson } from "./product";
7+
// The examples have been split into separate files for clarity.
8+
// See each file for more details about the specific example.
109

11-
import {
12-
OpenAIChatModel,
13-
ResponseFormat,
14-
SystemMessage,
15-
UserMessage,
16-
} from "@hypermode/modus-sdk-as/models/openai/chat";
17-
18-
// In this example, we will generate text using the OpenAI Chat model.
19-
// See https://platform.openai.com/docs/api-reference/chat/create for more details
20-
// about the options available on the model, which you can set on the input object.
21-
22-
// This model name should match the one defined in the modus.json manifest file.
23-
const modelName: string = "text-generator";
24-
25-
// This function generates some text based on the instruction and prompt provided.
26-
export function generateText(instruction: string, prompt: string): string {
27-
// The imported ChatModel interface follows the OpenAI Chat completion model input format.
28-
const model = models.getModel<OpenAIChatModel>(modelName);
29-
const input = model.createInput([
30-
new SystemMessage(instruction),
31-
new UserMessage(prompt),
32-
]);
33-
34-
// This is one of many optional parameters available for the OpenAI Chat model.
35-
input.temperature = 0.7;
36-
37-
// Here we invoke the model with the input we created.
38-
const output = model.invoke(input);
39-
40-
// The output is also specific to the ChatModel interface.
41-
// Here we return the trimmed content of the first choice.
42-
return output.choices[0].message.content.trim();
43-
}
44-
45-
// This function generates a single product.
46-
export function generateProduct(category: string): Product {
47-
// We can get creative with the instruction and prompt to guide the model
48-
// in generating the desired output. Here we provide a sample JSON of the
49-
// object we want the model to generate.
50-
const instruction = `Generate a product for the category provided.
51-
Only respond with valid JSON object in this format:
52-
${sampleProductJson}`;
53-
const prompt = `The category is "${category}".`;
54-
55-
// Set up the input for the model, creating messages for the instruction and prompt.
56-
const model = models.getModel<OpenAIChatModel>(modelName);
57-
const input = model.createInput([
58-
new SystemMessage(instruction),
59-
new UserMessage(prompt),
60-
]);
61-
62-
// Let's increase the temperature to get more creative responses.
63-
// Be careful though, if the temperature is too high, the model may generate invalid JSON.
64-
input.temperature = 1.2;
65-
66-
// This model also has a response format parameter that can be set to JSON,
67-
// Which, along with the instruction, can help guide the model in generating valid JSON output.
68-
input.responseFormat = ResponseFormat.Json;
69-
70-
// Here we invoke the model with the input we created.
71-
const output = model.invoke(input);
72-
73-
// The output should contain the JSON string we asked for.
74-
const json = output.choices[0].message.content.trim();
75-
const product = JSON.parse<Product>(json);
76-
return product;
77-
}
78-
79-
// This function generates multiple products.
80-
export function generateProducts(category: string, quantity: i32): Product[] {
81-
// Similar to the previous example above, we can tailor the instruction and prompt
82-
// to guide the model in generating the desired output. Note that understanding the behavior
83-
// of the model is important to get the desired results. In this case, we need the model
84-
// to return an _object_ containing an array, not an array of objects directly. That's because
85-
// the model will not reliably generate an array of objects directly.
86-
const instruction = `Generate ${quantity} products for the category provided.
87-
Only respond with valid JSON object containing a valid JSON array named 'list', in this format:
88-
{"list":[${sampleProductJson}]}`;
89-
const prompt = `The category is "${category}".`;
90-
91-
// Set up the input for the model, creating messages for the instruction and prompt.
92-
const model = models.getModel<OpenAIChatModel>(modelName);
93-
const input = model.createInput([
94-
new SystemMessage(instruction),
95-
new UserMessage(prompt),
96-
]);
97-
98-
// Adjust the model inputs, just like in the previous example.
99-
// Be careful, if the temperature is too high, the model may generate invalid JSON.
100-
input.temperature = 1.2;
101-
input.responseFormat = ResponseFormat.Json;
102-
103-
// Here we invoke the model with the input we created.
104-
const output = model.invoke(input);
105-
106-
// The output should contain the JSON string we asked for.
107-
const json = output.choices[0].message.content.trim();
108-
109-
// We can parse that JSON to a compatible object, to get the data we're looking for.
110-
const results = JSON.parse<Map<string, Product[]>>(json);
111-
return results.get("list");
112-
}
10+
export * from "./simple";
11+
export * from "./products";
12+
export * from "./media";
13+
export * from "./toolcalling";
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
/*
2+
* This example is part of the Modus project, licensed under the Apache License 2.0.
3+
* You may modify and use this example in accordance with the license.
4+
* See the LICENSE file that accompanied this code for further details.
5+
*/
6+
7+
import { models, http } from "@hypermode/modus-sdk-as";
8+
9+
import {
10+
OpenAIChatModel,
11+
DeveloperMessage,
12+
SystemMessage,
13+
UserMessage,
14+
TextContentPart,
15+
AudioContentPart,
16+
ImageContentPart,
17+
Image,
18+
Audio,
19+
} from "@hypermode/modus-sdk-as/models/openai/chat";
20+
21+
// These examples demonstrate how to use audio or image data with OpenAI chat models.
22+
// Currently, audio can be used for input or output, but images can be used only for input.
23+
24+
/**
25+
* This type is used in these examples to represent images or audio.
26+
*/
27+
class Media {
28+
// The content type of the media.
29+
contentType!: string;
30+
31+
// The binary data of the media.
32+
// This value will be base64 encoded when used in an API response.
33+
data!: Uint8Array;
34+
35+
// A text description or transcription of the media.
36+
text!: string;
37+
}
38+
39+
/**
40+
* This function generates an audio response based on the instruction and prompt provided.
41+
*/
42+
export function generateAudio(instruction: string, prompt: string): Media {
43+
// Note, this is similar to the generateText example, but with audio output requested.
44+
45+
// We'll generate the audio using an audio-enabled OpenAI chat model.
46+
const model = models.getModel<OpenAIChatModel>("audio-model");
47+
48+
const input = model.createInput([
49+
new SystemMessage(instruction),
50+
new UserMessage(prompt),
51+
]);
52+
53+
input.temperature = 0.7;
54+
55+
// Request audio output from the model.
56+
// Note, this is a convenience method that requests audio modality and sets the voice and format.
57+
// You can also set these values manually on the input object, if you prefer.
58+
input.requestAudioOutput("ash", "wav");
59+
60+
const output = model.invoke(input);
61+
62+
// Return the audio and its transcription.
63+
// Note that the message Content field will be empty for audio responses.
64+
// Instead, the text will be in the Message.Audio.Transcript field.
65+
const audio = output.choices[0].message.audio!;
66+
67+
const media = <Media>{
68+
contentType: "audio/wav",
69+
data: audio.data,
70+
text: audio.transcript.trim(),
71+
};
72+
73+
return media;
74+
}
75+
76+
/**
77+
* This function generates text that describes the image at the provided url.
78+
* In this example the image url is passed to the model, and the model retrieves the image.
79+
*/
80+
export function describeImage(url: string): string {
81+
// Note that because the model retrieves the image, any URL can be used.
82+
// However, this means that there is a risk of sending data to an unauthorized host, if the URL is not hardcoded or sanitized.
83+
// See the describeRandomImage function below for a safer approach.
84+
85+
const model = models.getModel<OpenAIChatModel>("text-generator");
86+
87+
const input = model.createInput([
88+
UserMessage.fromParts([
89+
new TextContentPart("Describe this image."),
90+
new ImageContentPart(Image.fromURL(url)),
91+
]),
92+
]);
93+
94+
const output = model.invoke(input);
95+
96+
return output.choices[0].message.content.trim();
97+
}
98+
99+
/**
100+
* This function fetches a random image, and then generates text that describes it.
101+
* In this example the image is retrieved by the function before passing it as data to the model.
102+
*/
103+
export function describeRandomImage(): Media {
104+
// Because this approach fetches the image directly, it is safer than the describeImage function above.
105+
// The host URL is allow-listed in the modus.json file, so we can trust the image source.
106+
107+
// Fetch a random image from the Picsum API. We'll just hardcode the size to make the demo simple to call.
108+
const response = http.fetch("https://picsum.photos/640/480");
109+
const data = Uint8Array.wrap(response.body);
110+
const contentType = response.headers.get("Content-Type")!;
111+
112+
// Describe the image using the OpenAI chat model.
113+
const model = models.getModel<OpenAIChatModel>("text-generator");
114+
115+
const input = model.createInput([
116+
UserMessage.fromParts([
117+
new TextContentPart("Describe this image."),
118+
new ImageContentPart(Image.fromData(data, contentType)),
119+
]),
120+
]);
121+
122+
input.temperature = 0.7;
123+
124+
const output = model.invoke(input);
125+
126+
// Return the image and its generated description.
127+
const text = output.choices[0].message.content.trim();
128+
const media = <Media>{
129+
contentType,
130+
data,
131+
text,
132+
};
133+
134+
return media;
135+
}
136+
137+
/**
138+
* This function fetches a random "Harvard Sentences" speech file from OpenSpeech, and then generates a transcript from it.
139+
* The sentences are from https://www.cs.columbia.edu/~hgs/audio/harvard.html
140+
*/
141+
export function transcribeRandomSpeech(): Media {
142+
// Pick a random file number from the list of available here:
143+
// https://www.voiptroubleshooter.com/open_speech/american.html
144+
const numbers: i32[] = [
145+
10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 30, 31, 32, 34, 35, 36, 37, 38, 39,
146+
40, 57, 58, 59, 60, 61,
147+
];
148+
const num = numbers[<i32>(Math.random() * numbers.length)];
149+
150+
// Fetch the speech file corresponding to the number.
151+
const url = `https://www.voiptroubleshooter.com/open_speech/american/OSR_us_000_00${num}_8k.wav`;
152+
const response = http.fetch(url);
153+
const data = Uint8Array.wrap(response.body);
154+
155+
// Transcribe the audio using an audio-enabled OpenAI chat model.
156+
const model = models.getModel<OpenAIChatModel>("audio-model");
157+
158+
const input = model.createInput([
159+
new DeveloperMessage(
160+
"Do not include any newlines or surrounding quotation marks in the response. Omit any explanation beyond the request.",
161+
),
162+
UserMessage.fromParts([
163+
new TextContentPart(
164+
"Provide an exact transcription of the contents of this audio file.",
165+
),
166+
new AudioContentPart(Audio.fromData(data, "wav")),
167+
]),
168+
]);
169+
170+
const output = model.invoke(input);
171+
172+
// Return the audio file and its transcript.
173+
const text = output.choices[0].message.content.trim();
174+
const media = <Media>{
175+
contentType: "audio/wav",
176+
data,
177+
text,
178+
};
179+
180+
return media;
181+
}

sdk/assemblyscript/examples/textgeneration/assembly/product.ts

Lines changed: 0 additions & 25 deletions
This file was deleted.

0 commit comments

Comments
 (0)