Skip to content

Commit 224599b

Browse files
authored
[ai] Add StreamText with an example (#510)
## Summary Adds StreamText and StreamTextStr to the top-level `ai` package. These are the streaming counterparts to GenerateText and GenerateTextStr. Adds an example showing the streaming case. ## How was it tested? Ran the example successfully. ## Community Contribution License All community contributions in this pull request are licensed to the project maintainers under the terms of the [Apache 2 License](https://www.apache.org/licenses/LICENSE-2.0). By creating this pull request I represent that I have the right to license the contributions to the project maintainers under the Apache 2 License as stated in the [Community Contribution License](https://github.com/jetify-com/opensource/blob/main/CONTRIBUTING.md#community-contribution-license).
1 parent 988405e commit 224599b

3 files changed

Lines changed: 117 additions & 1 deletion

File tree

aisdk/ai/ai.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,59 @@ func GenerateTextStr(ctx context.Context, prompt string, opts ...GenerateOption)
6161
func generate(ctx context.Context, prompt []api.Message, opts GenerateOptions) (api.Response, error) {
6262
return opts.Model.Generate(ctx, prompt, opts.CallOptions)
6363
}
64+
65+
// StreamText uses a language model to generate a streaming text response from a given prompt.
66+
//
67+
// This function streams its output as a sequence of events.
68+
//
69+
// It returns a [api.StreamResponse] containing a stream of events from the model,
70+
// including partial text, tool calls, and additional information.
71+
//
72+
// A prompt is a sequence of [api.Message]s:
73+
//
74+
// StreamText(ctx, []api.Message{
75+
// &api.UserMessage{
76+
// Content: []api.ContentBlock{
77+
// &api.TextBlock{Text: "Show me a picture of a cat"},
78+
// },
79+
// },
80+
// &api.AssistantMessage{
81+
// Content: []api.ContentBlock{
82+
// &api.TextBlock{Text: "Here is a picture of a cat"},
83+
// &api.ImageBlock{URL: "https://example.com/cat.png"},
84+
// },
85+
// },
86+
// })
87+
//
88+
// The last argument can optionally be a series of [GenerateOption] arguments:
89+
//
90+
// StreamText(ctx, messages, WithMaxTokens(100))
91+
func StreamText(ctx context.Context, prompt []api.Message, opts ...GenerateOption) (api.StreamResponse, error) {
92+
config := buildGenerateConfig(opts)
93+
return stream(ctx, prompt, config)
94+
}
95+
96+
// StreamTextStr uses a language model to generate a streaming text response from a given string prompt.
97+
//
98+
// It is a convenience wrapper around StreamText for simple string-based prompts.
99+
//
100+
// Example usage:
101+
//
102+
// StreamTextStr(ctx, "Write a brief summary of the benefits of renewable energy")
103+
//
104+
// The function can optionally take [GenerateOption] arguments:
105+
//
106+
// StreamTextStr(ctx, "Explain the key differences between REST and GraphQL APIs", WithMaxTokens(500))
107+
//
108+
// The string prompt is automatically converted to a [api.UserMessage] before
109+
// being passed to StreamText.
110+
func StreamTextStr(ctx context.Context, prompt string, opts ...GenerateOption) (api.StreamResponse, error) {
111+
msg := api.UserMessage{
112+
Content: []api.ContentBlock{api.TextBlock{Text: prompt}},
113+
}
114+
return StreamText(ctx, []api.Message{msg}, opts...)
115+
}
116+
117+
func stream(ctx context.Context, prompt []api.Message, opts GenerateOptions) (api.StreamResponse, error) {
118+
return opts.Model.Stream(ctx, prompt, opts.CallOptions)
119+
}

aisdk/ai/examples/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ Get your API keys from:
2020
| Example | Description |
2121
|---------|-------------|
2222
| [**simple-text**](basic/simple-text/) | Generate text from a simple string prompt |
23+
| [**streaming-text**](basic/streaming-text/) | Stream text responses in real-time |
2324

2425
### More Examples Coming Soon
2526

2627
- **Conversation** - Multi-message conversations with context
27-
- **Streaming** - Stream responses in real-time
2828
- **Multi-modal** - Working with images and files
2929
- **Tools** - Function calling and tool usage
3030
- **Advanced** - Production patterns and error handling
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"log"
7+
8+
"go.jetify.com/ai"
9+
"go.jetify.com/ai/api"
10+
"go.jetify.com/ai/provider/openai"
11+
)
12+
13+
func example() error {
14+
// Create a model
15+
model := openai.NewLanguageModel("gpt-4o-mini")
16+
17+
// Stream text
18+
response, err := ai.StreamTextStr(
19+
context.Background(),
20+
"Explain what artificial intelligence is in simple terms",
21+
ai.WithModel(model),
22+
ai.WithMaxOutputTokens(100),
23+
)
24+
if err != nil {
25+
return err
26+
}
27+
28+
// Print the streaming response:
29+
printStreamResponse(response)
30+
31+
return nil
32+
}
33+
34+
func printStreamResponse(response api.StreamResponse) {
35+
fmt.Print("AI: ")
36+
37+
for event := range response.Stream {
38+
switch e := event.(type) {
39+
case *api.TextDeltaEvent:
40+
// Print text delta events as they arrive
41+
fmt.Print(e.TextDelta)
42+
case *api.FinishEvent:
43+
// Print final information
44+
fmt.Printf("\n\nFinish Reason: %s\n", e.FinishReason)
45+
fmt.Printf("Usage: Input=%d, Output=%d, Total=%d tokens\n",
46+
e.Usage.InputTokens,
47+
e.Usage.OutputTokens,
48+
e.Usage.TotalTokens)
49+
case *api.ErrorEvent:
50+
// Handle errors
51+
fmt.Printf("\nError: %s\n", e.Error())
52+
}
53+
}
54+
}
55+
56+
func main() {
57+
if err := example(); err != nil {
58+
log.Fatal(err)
59+
}
60+
}

0 commit comments

Comments
 (0)