diff --git a/README.md b/README.md index 36eade3..2df984f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Ark Runtime Go SDK -The official Go library for the Ark runtime API. It provides convenient access to the Ark REST API from any Go application, with typed request/response models, streaming support, and built-in authentication. +The official Go library for accessing ModelArk on Volcengine and BytePlus. It provides typed request and response models, streaming, authentication, retries, and timeout configuration. ## Installation @@ -10,7 +10,37 @@ Requires **Go 1.20+**. go get github.com/volcengine/ark-runtime-go ``` -## Usage +## Choose Volcengine or BytePlus + +Set `ARK_API_KEY`, then choose the client factory for the service you use. The factory configures the correct base URL and region; request construction and all subsequent SDK calls are the same. + +### Volcengine (China) + +```go +client := arkruntime.NewVolcClient() +``` + +To pass the key directly: + +```go +client := arkruntime.NewVolcClientWithApiKey("your-api-key") +``` + +### BytePlus (BP) + +```go +client := arkruntime.NewByteplusClient() +``` + +To pass the key directly: + +```go +client := arkruntime.NewByteplusClientWithApiKey("your-api-key") +``` + +Use a model ID available in the corresponding Volcengine or BytePlus account. Model IDs can differ between the two services; the examples use `doubao-seed-2-1-pro-260628` for Volcengine and `seed-2-0-lite-260428` for BytePlus. Override either default with `ARK_MODEL`. + +## Quick start ### Responses API @@ -29,10 +59,10 @@ import ( ) func main() { - client := arkruntime.NewClientWithApiKey(os.Getenv("ARK_API_KEY")) + client := arkruntime.NewVolcClientWithApiKey(os.Getenv("ARK_API_KEY")) req := &responses.ResponsesRequest{ - Model: "doubao-seed-1-6", + Model: os.Getenv("ARK_MODEL"), Input: responses.NewStringResponsesInput("What is the capital of France?"), } @@ -44,6 +74,10 @@ func main() { } ``` +Set `ARK_MODEL` to a model ID from your account before running the example. + +## Usage + ### Chat Completions ```go @@ -59,10 +93,10 @@ import ( ) func main() { - client := arkruntime.NewClientWithApiKey(os.Getenv("ARK_API_KEY")) + client := arkruntime.NewVolcClientWithApiKey(os.Getenv("ARK_API_KEY")) req := &chat.ChatCompletionRequest{ - Model: "doubao-seed-1-6", + Model: os.Getenv("ARK_MODEL"), Messages: []chat.ChatCompletionRequestMessage{ { OneOf: chat.NewChatCompletionRequestUserMessageChatCompletionRequestMessageSum( @@ -186,7 +220,7 @@ sumTool := responses.Tool{ // 2. Send the request with tools req := &responses.ResponsesRequest{ - Model: "doubao-seed-1-6", + Model: os.Getenv("ARK_MODEL"), Input: responses.NewStringResponsesInput("What is 1 + 2?"), Tools: []responses.Tool{sumTool}, } @@ -203,25 +237,28 @@ req.Input = responses.NewInputItemArrayResponsesInput([]responses.InputItem{ }) ``` -See [examples/responses/function_call](./examples/responses/function_call) for a complete runnable example. +See [examples/volc/responses/function_call](./examples/volc/responses/function_call) or [examples/byteplus/responses/function_call](./examples/byteplus/responses/function_call) for a complete runnable example. ## Authentication ```go -// API key (recommended) — reads from code or from ARK_API_KEY env var -client := arkruntime.NewClientWithApiKey(os.Getenv("ARK_API_KEY")) +// API key (recommended) +client := arkruntime.NewVolcClientWithApiKey(os.Getenv("ARK_API_KEY")) +client := arkruntime.NewByteplusClientWithApiKey(os.Getenv("ARK_API_KEY")) // AK/SK authentication -client := arkruntime.NewClientWithAkSk( +client := arkruntime.NewVolcClientWithAkSk( os.Getenv("VOLC_ACCESSKEY"), os.Getenv("VOLC_SECRETKEY"), ) - -// Cloud-aware factories (auto-detect base URL and env vars) -client := arkruntime.NewVolcClient() -client := arkruntime.NewByteplusClient() +client := arkruntime.NewByteplusClientWithAkSk( + os.Getenv("BYTEPLUS_ACCESSKEY"), + os.Getenv("BYTEPLUS_SECRETKEY"), +) ``` +The no-argument cloud factories prefer `ARK_API_KEY` and otherwise use the cloud-specific AK/SK environment variables shown above. + ## Error handling API errors are returned as standard Go errors. Check for them using the usual `if err != nil` pattern. For streaming, `io.EOF` signals a clean end of the stream. @@ -234,56 +271,26 @@ if err != nil { } ``` -## API coverage - -| API | Methods | -|-----|---------| -| Responses | `client.CreateResponses()` / `client.CreateResponsesStream()` | -| Chat Completions | `client.CreateChatCompletion()` / `client.CreateChatCompletionStream()` | -| Embeddings | `client.CreateEmbeddings()` | -| Multimodal Embeddings | `client.CreateMultiModalEmbeddings()` | -| Content Generation | `client.CreateContentGenerationTask()` | -| Images | `client.CreateImageGeneration()` | -| Files | `client.CreateFile()` / `client.ListFiles()` / `client.DeleteFile()` | -| Tokenization | `client.CreateTokenization()` | - -## Package layout - -``` -arkruntime/ Client, auth, retries, streaming -arkruntime/model/responses/ Responses API types -arkruntime/model/chat/ Chat Completions API types -arkruntime/model/embedding/ Text Embedding API types -arkruntime/model/multimodalembedding/ Multimodal Embedding API types -arkruntime/model/contentgeneration/ Content Generation API types -arkruntime/model/images/ Image Generation API types -arkruntime/model/tokenization/ Tokenization API types -arkruntime/model/file/ Files API types -``` - ## Examples +For detailed usage guidance and legacy migration, see +[`docs/README.md`](docs/README.md) and +[`docs/migration.md`](docs/migration.md). + Runnable examples are in the [examples/](./examples) directory: -- [responses/basic](./examples/responses/basic) — streaming responses with multi-turn chaining -- [responses/function_call](./examples/responses/function_call) — tool use with local execution -- [responses/web_search](./examples/responses/web_search) — built-in web search tool -- [responses/video](./examples/responses/video) — video upload and analysis -- [responses/mcp](./examples/responses/mcp) — remote MCP server integration -- [chat/basic](./examples/chat/basic) — standard and streaming chat completions -- [embeddings](./examples/embeddings) — text embeddings -- [multimodalembeddings](./examples/multimodalembeddings) — image embeddings -- [contentgeneration](./examples/contentgeneration) — video generation tasks -- [files](./examples/files) — file upload, list, and delete -- [tokenization](./examples/tokenization) — tokenize text and inspect tokens +- [volc](./examples/volc) — Volcengine China examples for Chat, Responses, images, video generation, embeddings, files, tokenization, batch APIs, and resource APIs +- [byteplus](./examples/byteplus) — BytePlus counterparts using the BytePlus client and regional model IDs + +MCP examples are provided for both clouds and show the required `ark-beta-mcp: true` header. Other built-in-tool examples are CN-only and show their corresponding beta headers. Run any example with: ```bash -ARK_API_KEY=your-key go run ./examples/responses/basic +ARK_API_KEY=your-key go run examples/volc/responses/basic/main.go ``` ## Requirements - Go 1.20 or later -- An Ark API key (set via `ARK_API_KEY` environment variable or passed directly to the client) +- A Volcengine or BytePlus ModelArk API key diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..0655ea3 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,46 @@ +# Ark Runtime Go SDK documentation + +This directory contains detailed usage and migration guidance for the Ark +Runtime Go SDK. + +## Choose the right document + +- [Usage guide](usage.md): install the SDK, select + Volcengine (CN) or BytePlus, construct typed requests, handle streams, and + use built-in tools safely. +- [Migration guide](migration.md): move an application from the legacy + Volcengine or BytePlus Go SDK to this SDK. +- [`../examples/volc`](../examples/volc): runnable Volcengine examples. +- [`../examples/byteplus`](../examples/byteplus): runnable BytePlus examples. + +## Important usage rules + +1. Select the cloud once when creating the client. Use + `NewVolcClientWithApiKey` for CN and `NewByteplusClientWithApiKey` for + BytePlus. Do not copy a base URL between clouds. +2. Keep credentials in `ARK_API_KEY`; never place an API key in source code, + generated patches, logs, or tests. +3. Use the generated request and union constructors. Do not assemble JSON and + send it through an unrelated HTTP client unless the application explicitly + requires raw HTTP. +4. Treat stream events as variants. Ignore unknown variants so applications + remain compatible when the service adds events. +5. MCP is available in CN and BytePlus. Other built-in Responses tools in the + examples are CN-only. Send the matching `ark-beta-*` header on every request + that uses a beta tool. +6. Prefer an application-provided model or endpoint ID. The model names in the + examples are runnable defaults, not values to hard-code into a library. + +## Minimal verification + +After a change, run: + +```bash +go test ./... +go vet ./... +``` + +For a migrated application, also run one non-streaming and one streaming +request in the intended cloud. Built-in tool paths need their own smoke test +because a successful ordinary Responses request does not validate tool access +or beta headers. diff --git a/docs/migration.md b/docs/migration.md new file mode 100644 index 0000000..e6089b6 --- /dev/null +++ b/docs/migration.md @@ -0,0 +1,161 @@ +# Migrate from the legacy Go SDK + +This guide covers both legacy packages: + +- `github.com/volcengine/volcengine-go-sdk/service/arkruntime` +- `github.com/byteplus-sdk/byteplus-go-sdk-v2/service/arkruntime` + +The new package is `github.com/volcengine/ark-runtime-go/arkruntime` for both +clouds. Migration is partly mechanical and partly semantic because the new SDK +uses generated, typed unions. + +## 1. Migration order + +Migrate one API flow at a time: + +1. Choose the target cloud: Volcengine (CN) or BytePlus. +2. Update the Ark Runtime import paths and regional client constructor. +3. Move each request to its new API-specific generated model package. +4. Rebuild union-valued request fields with the generated constructors. +5. Update non-streaming response access and streaming event dispatch. +6. Add the required beta header to every built-in-tool request and remove any + CN-only tool from a BytePlus target. +7. Compile and smoke-test that flow before migrating the next one. + +Do not use a project-wide regular-expression replacement for request models or +stream events. Their correct mapping depends on the selected union variant and +the events the application consumes. + +## 2. Dependency, imports, and client + +| Legacy | New | +|---|---| +| `github.com/volcengine/volcengine-go-sdk/service/arkruntime` | `github.com/volcengine/ark-runtime-go/arkruntime` | +| `github.com/byteplus-sdk/byteplus-go-sdk-v2/service/arkruntime` | `github.com/volcengine/ark-runtime-go/arkruntime` | +| `.../service/arkruntime/model/responses` | `github.com/volcengine/ark-runtime-go/arkruntime/model/responses` | +| `NewClientWithApiKey(key)` | CN: `NewVolcClientWithApiKey(key)`; BP: `NewByteplusClientWithApiKey(key)` | + +After imports are updated, run `go mod tidy`. Remove the legacy root SDK only +when no other service in the application imports it. + +The legacy `service/arkruntime/model` root package does not have a one-to-one +replacement. Pick the new API-specific package such as `model/chat`, +`model/responses`, `model/embeddings`, or `model/images`. + +## 3. Chat request mapping + +Legacy Chat used `model.CreateChatCompletionRequest`, pointer message slices, +and pointer-based content. New Chat uses the `model/chat` package and explicit +message variants. + +```go +// New SDK +req := &chat.ChatCompletionRequest{ + Model: model, + Messages: []chat.ChatCompletionRequestMessage{ + {OneOf: chat.NewChatCompletionRequestUserMessageChatCompletionRequestMessageSum( + chat.ChatCompletionRequestUserMessage{ + Role: chat.ChatCompletionRequestUserMessageRoleUser, + Content: chat.NewStringChatCompletionMessageContent("Hello"), + }, + )}, + }, +} +``` + +The service method names remain recognizable: + +| Legacy | New | +|---|---| +| `CreateChatCompletion(ctx, req)` | `CreateChatCompletion(ctx, &req)` | +| `CreateChatCompletionStream(ctx, req)` | `CreateChatCompletionStream(ctx, &req)` | +| `choice.Message.Content.StringValue` | `choice.Message.Content` | +| streaming `choice.Delta.Content` string | `choice.Delta.Content.Or("")` | + +Use the regional basic Chat example as the canonical full conversion. + +## 4. Responses request mapping + +The main request maps from the legacy `responses.ResponsesRequest` to the new +type with the same name. Its union-valued fields change: + +| Legacy shape | New shape | +|---|---| +| `ResponsesInput_StringValue` | `responses.NewStringResponsesInput(value)` | +| `ResponsesInput_ListValue` | `responses.NewInputItemArrayResponsesInput(items)` | +| `InputItem_InputMessage` | `responses.NewItemEasyMessageInputItemSum(message)` | +| `ContentItem_Text` | `responses.NewContentItemTextContentItemSum(text)` | +| pointer optional scalar | generated `responses.NewOpt*` helper | +| `PreviousResponseId` | `PreviousResponseID` | + +Build from the leaves inward: content variant, message, input-item variant, +input list, then `ResponsesRequest`. This preserves the JSON discriminator and +payload together. + +Do not copy a serialized legacy request body into a new struct. If an +application stores raw JSON templates, unmarshal them into the new type in a +test and compare the emitted JSON with the intended API body. + +## 5. Stream-event mapping + +Both SDKs use `stream.Recv()`, but the event representation changed. + +| Legacy | New | +|---|---| +| `*responses.Event` | `*responses.ResponseStreamEvent` | +| `event.GetEventType()` | `event.OneOf.Type` | +| `event.GetText().GetDelta()` | `event.OneOf.ResponseTextDeltaEvent.Delta.Or("")` | +| `event.GetResponse()...GetId()` | read `ResponseCreatedEvent` or `ResponseCompletedEvent` variant | + +Dispatch only on events the application consumes and retain a `default` case. +For function calling, capture the call ID from the output-item-done variant and +the response ID from a response event; send a function-call-output input item +in the next request. For MCP approvals, preserve both the approval request ID +and previous response ID. + +## 6. Extra headers and regional behavior + +Headers are call options in the new SDK: + +```go +client.CreateResponses(ctx, req, + arkruntime.WithCustomHeader("ark-beta-mcp", "true")) +client.CreateResponsesStream(ctx, req, + arkruntime.WithCustomHeader("ark-beta-mcp", "true")) +``` + +Use `ark-beta-mcp` for MCP in either cloud. Web search +(`ark-beta-web-search`), knowledge search (`ark-beta-knowledge-search`), Doubao +App (`ark-beta-doubao-app`), and image process (`ark-beta-image-process`) are +CN-only. Do not silently drop a tool or header when migrating; fail migration +review if a CN-only tool appears in a BytePlus target. + +## 7. Regional model IDs + +Model names and endpoint IDs are cloud-specific. Prefer application +configuration, and update any legacy hard-coded default when changing clouds: + +| API | Volcengine (CN) example | BytePlus example | +|---|---|---| +| Responses / Chat | `doubao-seed-2-1-pro-260628` | `seed-2-0-lite-260428` | +| Multimodal / sparse embeddings | `doubao-embedding-vision-251215` | `skylark-embedding-vision-251215` | +| Image generation | `doubao-seedream-5-0-pro-260628` | `dola-seedream-5-0-pro-260628` | +| Video generation | `doubao-seedance-2-0-fast-260128` | `dreamina-seedance-2-0-fast-260128` | + +Use a model or endpoint ID provisioned for the target account if it differs +from these example defaults. + +## 8. Validate the migration + +1. Search the application for legacy imports and generic client constructors; + none should remain in migrated Ark Runtime code. +2. Run `gofmt` on changed Go files, then `go mod tidy`. +3. Run `go test ./...` and `go vet ./...`. +4. Run a non-streaming request and check the returned content. +5. Run a streaming request through completion and verify error handling. +6. Smoke-test each built-in tool and confirm its beta header is present. +7. Run the same checks separately for CN and BytePlus if the application + supports both; do not reuse a key, model, endpoint ID, or client across them. + +Review every migrated request and event handler against the corresponding +regional example before calling the migration complete. diff --git a/docs/usage.md b/docs/usage.md new file mode 100644 index 0000000..a7ebd7c --- /dev/null +++ b/docs/usage.md @@ -0,0 +1,195 @@ +# Usage guide + +Use this guide when creating or editing a Go application with Ark Runtime. The +repository examples are the source of truth for complete request shapes. + +## 1. Install and configure + +```bash +go get github.com/volcengine/ark-runtime-go@latest +# Set ARK_API_KEY in the process environment before running the application. +``` + +Keep the key outside source control. Let the application accept a model or +endpoint ID through configuration, for example `ARK_MODEL`. + +## 2. Select the cloud + +Only the client constructor changes. Request construction and response +handling stay the same. + +```go +// Volcengine (CN) +client := arkruntime.NewVolcClientWithApiKey(os.Getenv("ARK_API_KEY")) + +// BytePlus +client := arkruntime.NewByteplusClientWithApiKey(os.Getenv("ARK_API_KEY")) +``` + +Use the models provisioned for that cloud. Current example defaults include: + +| API | Volcengine (CN) | BytePlus | +|---|---|---| +| Responses / Chat | `doubao-seed-2-1-pro-260628` | `seed-2-0-lite-260428` | +| Multimodal / sparse embeddings | `doubao-embedding-vision-251215` | `skylark-embedding-vision-251215` | +| Image generation | `doubao-seedream-5-0-pro-260628` | `dola-seedream-5-0-pro-260628` | +| Video generation | `doubao-seedance-2-0-fast-260128` | `dreamina-seedance-2-0-fast-260128` | + +An account may expose a different model name or an endpoint ID. Configuration +from the user takes precedence over this table. + +BytePlus currently has no model for the text-only `/embeddings` endpoint, so +its examples use `/embeddings/multimodal` instead. + +## 3. Build typed requests + +Generated models represent JSON unions explicitly. Always use the matching +constructor instead of filling the union internals by hand. + +For a simple Responses input: + +```go +req := &responses.ResponsesRequest{ + Model: model, + Input: responses.NewStringResponsesInput("Explain LLMs in one sentence."), +} +response, err := client.CreateResponses(ctx, req) +``` + +For a structured Responses input, create the leaf content, wrap it as an input +item, and then wrap the list: + +```go +message := responses.ItemEasyMessage{ + Role: responses.NewOptMessageRole(responses.MessageRoleUser), + Content: responses.NewContentItemArrayMessageContent([]responses.ContentItem{ + {OneOf: responses.NewContentItemTextContentItemSum( + responses.ContentItemText{ + Type: responses.ContentItemTextTypeInputText, + Text: "Describe this request", + }, + )}, + }), +} +req.Input = responses.NewInputItemArrayResponsesInput([]responses.InputItem{ + {OneOf: responses.NewItemEasyMessageInputItemSum(message)}, +}) +``` + +Chat messages use the same pattern. See +[`examples/volc/chat/basic`](../examples/volc/chat/basic) or the corresponding +[`examples/byteplus/chat/basic`](../examples/byteplus/chat/basic) directory. + +Practical rules: + +- Pass request structs by pointer. +- Use `NewOpt*` helpers for optional generated fields. +- Use the generated `New*Sum` constructor for a union variant. +- Do not set `OneOf.Type` separately from its value. +- Preserve user-provided extension fields and custom headers during refactors. + +## 4. Handle responses and streams + +Non-streaming Responses output is a list of typed output-item variants. Check +the variant before reading its fields. The full extraction pattern is in the +basic Responses examples. + +Chat streams yield chunks: + +```go +stream, err := client.CreateChatCompletionStream(ctx, req) +if err != nil { /* handle */ } +defer stream.Close() + +for { + chunk, err := stream.Recv() + if err == io.EOF { + break + } + if err != nil { /* handle */ } + if len(chunk.Choices) > 0 { + fmt.Print(chunk.Choices[0].Delta.Content.Or("")) + } +} +``` + +Responses streams yield `*responses.ResponseStreamEvent`. Dispatch on +`event.OneOf.Type`: + +```go +switch event.OneOf.Type { +case responses.ResponseTextDeltaEventResponseStreamEventSum: + fmt.Print(event.OneOf.ResponseTextDeltaEvent.Delta.Or("")) +case responses.ResponseCompletedEventResponseStreamEventSum: + responseID := event.OneOf.ResponseCompletedEvent.Response.ID + _ = responseID +default: + // Forward-compatible: ignore events this application does not consume. +} +``` + +Do not assume that every event contains text. Reasoning, output-item, +function-call, MCP, completion, and error events carry different payloads. +Keep `io.EOF` separate from an actual stream error and close every stream. + +## 5. Built-in tools and headers + +Pass beta headers as request options on both streaming and non-streaming calls: + +```go +response, err := client.CreateResponses( + ctx, + req, + arkruntime.WithCustomHeader("ark-beta-mcp", "true"), +) +``` + +| Tool | Cloud | Required header | +|---|---|---| +| MCP | CN and BytePlus | `ark-beta-mcp: true` | +| Web search | CN only | `ark-beta-web-search: true` | +| Knowledge search | CN only | `ark-beta-knowledge-search: true` | +| Doubao App | CN only | `ark-beta-doubao-app: true` | +| Image process | CN only | `ark-beta-image-process: true` | + +Do not migrate a CN-only built-in tool to BytePlus. Function calling is an +application-defined tool flow and is not the same as a hosted built-in tool. + +## 6. Choose an example + +Examples are divided by cloud, then API. Start with the matching directory and +retain its constructor, model family, and tool availability: + +- Volcengine: [`examples/volc`](../examples/volc) +- BytePlus: [`examples/byteplus`](../examples/byteplus) + +Use this routing table instead of adapting an unrelated Chat or Responses +example: + +| Intent | Example path below the region directory | +|---|---| +| Chat, including stream/non-stream/function calling | `chat/` | +| Responses, including stream/non-stream/tools | `responses/` | +| Text embeddings (Volcengine only) | `embeddings/` | +| Sparse or multimodal embeddings | `sparseembeddings/`, `multimodalembeddings/` | +| Image generation | `images/` | +| Video generation | `contentgeneration/` | +| File upload and operations | `files/` | +| Batch inference | `batch/` and the `batch_*` examples | +| Agents, sessions, memory stores, environments | the matching lifecycle example | +| Token counting | `tokenization/` | + +Some historical directory names use different separators. Prefer the example +listed by the regional README or the one that imports this SDK's generated +models. Chat and Responses include both streaming and non-streaming flows. + +## 7. Completion checklist + +- The dependency uses `github.com/volcengine/ark-runtime-go` only. +- Exactly one regional constructor is selected by application configuration. +- No secret is committed. +- Request unions use generated constructors. +- Streaming code handles `io.EOF`, errors, and unknown event variants. +- Every built-in tool request carries its matching beta header. +- A CN-only tool is not present in BytePlus code. +- `go test ./...` and `go vet ./...` pass. diff --git a/examples/README.md b/examples/README.md index 49131f4..f900810 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,24 +1,20 @@ # Examples -Runnable examples for the `arkruntime` Go SDK. Each expects `ARK_API_KEY` in the env: +Runnable examples for the `arkruntime` Go SDK. Set `ARK_API_KEY` and, for most examples, `ARK_MODEL` to a model ID available in your account. + +Run commands from the repository root: ```bash export ARK_API_KEY=... -go run examples/responses/basic/main.go +export ARK_MODEL=... +go run examples/volc/responses/basic/main.go ``` -| Dir | What it shows | -|---|---| -| responses/basic/ | basic streaming responses with two-round conversation on POST /v1/responses | -| listinputitems/ | GET /v1/responses/{id}/input_items | -| multimodalembeddings/ | POST /embeddings/multimodal with an image input | -| sparseembeddings/ | POST /embeddings/multimodal with sparse embedding output enabled | -| images/ | POST /images/generations — Seedream T2I, Seededit edit-from-image, sequential image generation | -| agents/ | Managed-Agents: Agent lifecycle — Create/Get/List/Update/ListVersions/Delete | -| environments/ | Managed-Agents: Environment lifecycle — Create/Get/List/Update/Delete (cloud + unrestricted networking) | -| sessions_loop/ | Managed-Agents: end-to-end agent loop — Agent + Env + Session, send user.message, stream events until idle | -| memory_stores/ | Managed-Agents: MemoryStore + nested Memory CRUD | +All service-calling examples are grouped by cloud: + +- [`volc/`](./volc) uses `NewVolcClientWithApiKey` and Volcengine China model IDs. +- [`byteplus/`](./byteplus) uses `NewByteplusClientWithApiKey` and BytePlus model IDs. -The Managed-Agents examples additionally accept `ARK_MODEL_ID` for the model id (falls back to a `${YOUR_MODEL_ID}` placeholder that will 400 at runtime). +The paired multimodal and sparse embedding examples default to `doubao-embedding-vision-251215` / `skylark-embedding-vision-251215`. The paired image examples default to `doubao-seedream-5-0-pro-260628` / `dola-seedream-5-0-pro-260628`. The paired video-generation examples default to `doubao-seedance-2-0-fast-260128` / `dreamina-seedance-2-0-fast-260128`. -Only currently-implemented APIs have runnable examples. See the `API Coverage` table in the top-level README for the roadmap. +MCP is available in both clouds and its examples explicitly send `ark-beta-mcp: true`. Other built-in tools are CN-only: Web Search sends `ark-beta-web-search: true`, and Doubao App sends `ark-beta-doubao-app: true`. diff --git a/examples/byteplus/agents/main.go b/examples/byteplus/agents/main.go new file mode 100644 index 0000000..9e8a085 --- /dev/null +++ b/examples/byteplus/agents/main.go @@ -0,0 +1,93 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +// Agent lifecycle example — Create → Get → List → Update → ListVersions → Delete. +// +// Runs against the outward /api/v3/agents endpoint. Requires: +// +// export ARK_API_KEY=... +// export ARK_MODEL_ID=seed-2-0-lite-260428 # or whatever you have access to +// go run examples/agents/main.go +package main + +import ( + "context" + "fmt" + "log" + "os" + "time" + + "github.com/volcengine/ark-runtime-go/arkruntime" + "github.com/volcengine/ark-runtime-go/arkruntime/model/agent" +) + +func main() { + apiKey := os.Getenv("ARK_API_KEY") + if apiKey == "" { + log.Fatal("set ARK_API_KEY") + } + modelID := os.Getenv("ARK_MODEL_ID") + if modelID == "" { + modelID = "${YOUR_MODEL_ID}" + } + + client := arkruntime.NewByteplusClientWithApiKey(apiKey) + ctx := context.Background() + + // 1. Create + name := fmt.Sprintf("example-agent-%d", time.Now().UnixNano()) + created, err := client.CreateAgent(ctx, &agent.CreateAgentRequest{ + Name: name, + Model: agent.ModelConfig{ID: modelID}, + Description: agent.NewOptString("created by ark-runtime-go example"), + }) + if err != nil { + log.Fatalf("create agent: %v", err) + } + fmt.Printf("created: id=%s version=%d name=%s\n", created.ID, created.Version, created.Name) + + // Register cleanup up-front so we don't leak an agent if a later step fails. + defer func() { + if _, err := client.DeleteAgent(context.Background(), created.ID); err != nil { + log.Printf("cleanup delete_agent(%s): %v", created.ID, err) + } else { + fmt.Printf("deleted: id=%s\n", created.ID) + } + }() + + // 2. Get + got, err := client.GetAgent(ctx, created.ID) + if err != nil { + log.Fatalf("get agent: %v", err) + } + fmt.Printf("get: id=%s name=%s\n", got.ID, got.Name) + + // 3. List — takes limit / page / created_at_gte / created_at_lte. + listed, err := client.ListAgents(ctx, &agent.AgentsListParams{ + Limit: agent.NewOptInt32(5), + }) + if err != nil { + log.Fatalf("list agents: %v", err) + } + fmt.Printf("list: %d items, next_page=%q\n", len(listed.Data), listed.NextPage.Value) + + // 4. Update — bumps version. Requires the previous version for optimistic + // concurrency control. + updated, err := client.UpdateAgent(ctx, created.ID, &agent.UpdateAgentRequest{ + Version: created.Version, + Description: agent.NewOptString("updated by ark-runtime-go example"), + }) + if err != nil { + log.Fatalf("update agent: %v", err) + } + fmt.Printf("updated: id=%s version=%d (was %d)\n", updated.ID, updated.Version, created.Version) + + // 5. List versions — should see at least v1 (create) + v2 (update). + versions, err := client.ListAgentVersions(ctx, created.ID, &agent.AgentsListVersionsParams{ + Limit: agent.NewOptInt32(10), + }) + if err != nil { + log.Fatalf("list agent versions: %v", err) + } + fmt.Printf("versions: %d items\n", len(versions.Data)) +} diff --git a/examples/byteplus/batch_chat/main.go b/examples/byteplus/batch_chat/main.go new file mode 100644 index 0000000..d3aa5f2 --- /dev/null +++ b/examples/byteplus/batch_chat/main.go @@ -0,0 +1,107 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "context" + "encoding/json" + "fmt" + "os" + "sync" + "time" + + "github.com/volcengine/ark-runtime-go/arkruntime" + "github.com/volcengine/ark-runtime-go/arkruntime/model/chat" +) + +/** + * Authentication + * If you authorize your endpoint using an API key, you can set your api key to environment variable "ARK_API_KEY" + * client := arkruntime.NewByteplusClientWithApiKey(os.Getenv("ARK_API_KEY")) + * + * Batch chat completions use the same request/response shapes as + * /chat/completions, but are dispatched against /batch/chat/completions + * with a high-concurrency HTTP client and a per-model breaker that + * honours Retry-After headers. Streaming is not supported. + */ + +func main() { + client := arkruntime.NewByteplusClientWithApiKey( + os.Getenv("ARK_API_KEY"), + arkruntime.WithBatchMaxParallel(3000), // max parallel in-flight batch requests + ) + + // In a real program, load requests from a file, queue, or DB. Here we + // fan out a small fixed set so the example runs end-to-end. + const total = 20 + requests := mockRequests("${YOUR_ENDPOINT_ID}", total) + + // Global deadline: if exceeded, every outstanding request is cancelled. + ctx, cancel := context.WithTimeout(context.Background(), 24*time.Hour) + defer cancel() + + var wg sync.WaitGroup + for req := range requests { + wg.Add(1) + go func(req *chat.ChatCompletionRequest) { + defer wg.Done() + + // Per-request deadline on top of the global one. + reqCtx, reqCancel := context.WithTimeout(ctx, 10*time.Minute) + defer reqCancel() + + resp, err := client.CreateBatchChatCompletion(reqCtx, req) + if err != nil { + fmt.Fprintln(os.Stderr, err) + return + } + fmt.Println(mustMarshalJSON(resp)) + }(req) + } + wg.Wait() +} + +func mockRequests(endpoint string, count int) <-chan *chat.ChatCompletionRequest { + out := make(chan *chat.ChatCompletionRequest) + go func() { + defer close(out) + for i := 0; i < count; i++ { + out <- &chat.ChatCompletionRequest{ + Model: endpoint, + Messages: []chat.ChatCompletionRequestMessage{ + systemMsg("你是豆包,是由字节跳动开发的 AI 人工智能助手"), + userMsg("常见的十字花科植物有哪些?"), + }, + } + } + }() + return out +} + +func systemMsg(content string) chat.ChatCompletionRequestMessage { + return chat.ChatCompletionRequestMessage{ + OneOf: chat.NewChatCompletionRequestSystemMessageChatCompletionRequestMessageSum( + chat.ChatCompletionRequestSystemMessage{ + Role: chat.ChatCompletionRequestSystemMessageRoleSystem, + Content: chat.NewStringChatCompletionMessageContent(content), + }, + ), + } +} + +func userMsg(content string) chat.ChatCompletionRequestMessage { + return chat.ChatCompletionRequestMessage{ + OneOf: chat.NewChatCompletionRequestUserMessageChatCompletionRequestMessageSum( + chat.ChatCompletionRequestUserMessage{ + Role: chat.ChatCompletionRequestUserMessageRoleUser, + Content: chat.NewStringChatCompletionMessageContent(content), + }, + ), + } +} + +func mustMarshalJSON(v interface{}) string { + b, _ := json.Marshal(v) + return string(b) +} diff --git a/examples/byteplus/batch_multimodalembeddings/main.go b/examples/byteplus/batch_multimodalembeddings/main.go new file mode 100644 index 0000000..f863845 --- /dev/null +++ b/examples/byteplus/batch_multimodalembeddings/main.go @@ -0,0 +1,85 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "context" + "encoding/json" + "fmt" + "os" + "sync" + "time" + + "github.com/volcengine/ark-runtime-go/arkruntime" + "github.com/volcengine/ark-runtime-go/arkruntime/model/multimodalembedding" +) + +/** + * Authentication + * If you authorize your endpoint using an API key, you can set your api key to environment variable "ARK_API_KEY" + * client := arkruntime.NewByteplusClientWithApiKey(os.Getenv("ARK_API_KEY")) + * + * Batch multimodal embeddings use the same request/response shapes as + * /embeddings/multimodal, but are dispatched against + * /batch/embeddings/multimodal with a high-concurrency HTTP client and a + * per-model breaker that honours Retry-After headers. + */ + +func main() { + client := arkruntime.NewByteplusClientWithApiKey( + os.Getenv("ARK_API_KEY"), + arkruntime.WithBatchMaxParallel(3000), + ) + + const total = 20 + requests := mockRequests("skylark-embedding-vision-251215", total) + + ctx, cancel := context.WithTimeout(context.Background(), 24*time.Hour) + defer cancel() + + var wg sync.WaitGroup + for req := range requests { + wg.Add(1) + go func(req *multimodalembedding.MultiModalEmbeddingRequest) { + defer wg.Done() + + reqCtx, reqCancel := context.WithTimeout(ctx, 10*time.Minute) + defer reqCancel() + + resp, err := client.CreateBatchMultiModalEmbeddings(reqCtx, req) + if err != nil { + fmt.Fprintln(os.Stderr, err) + return + } + fmt.Println(mustMarshalJSON(resp)) + }(req) + } + wg.Wait() +} + +func mockRequests(model string, count int) <-chan *multimodalembedding.MultiModalEmbeddingRequest { + out := make(chan *multimodalembedding.MultiModalEmbeddingRequest) + go func() { + defer close(out) + for i := 0; i < count; i++ { + out <- &multimodalembedding.MultiModalEmbeddingRequest{ + Model: model, + Input: []multimodalembedding.EmbeddingInput{ + { + Type: multimodalembedding.EmbeddingInputTypeImageURL, + ImageURL: multimodalembedding.NewOptImageURL(multimodalembedding.ImageURL{ + URL: "https://ark-project.tos-cn-beijing.volces.com/images/view.jpeg", + }), + }, + }, + } + } + }() + return out +} + +func mustMarshalJSON(v interface{}) string { + b, _ := json.Marshal(v) + return string(b) +} diff --git a/examples/byteplus/chat/basic/main.go b/examples/byteplus/chat/basic/main.go new file mode 100644 index 0000000..a68bf1a --- /dev/null +++ b/examples/byteplus/chat/basic/main.go @@ -0,0 +1,88 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "context" + "fmt" + "io" + "os" + + "github.com/volcengine/ark-runtime-go/arkruntime" + "github.com/volcengine/ark-runtime-go/arkruntime/model/chat" +) + +/** + * Authentication + * If you authorize your endpoint using an API key, you can set your api key to environment variable "ARK_API_KEY" + * client := arkruntime.NewByteplusClientWithApiKey(os.Getenv("ARK_API_KEY")) + */ + +func main() { + client := arkruntime.NewByteplusClientWithApiKey(os.Getenv("ARK_API_KEY")) + ctx := context.Background() + + messages := []chat.ChatCompletionRequestMessage{ + systemMsg("你是豆包,是由字节跳动开发的 AI 人工智能助手"), + userMsg("常见的十字花科植物有哪些?"), + } + + fmt.Println("----- standard request -----") + req := &chat.ChatCompletionRequest{ + Model: "seed-2-0-lite-260428", + Messages: messages, + } + resp, err := client.CreateChatCompletion(ctx, req) + if err != nil { + fmt.Printf("standard chat error: %v\n", err) + return + } + if len(resp.Choices) > 0 { + fmt.Println(resp.Choices[0].Message.Content) + } + + fmt.Println("----- streaming request -----") + stream, err := client.CreateChatCompletionStream(ctx, req) + if err != nil { + fmt.Printf("stream chat error: %v\n", err) + return + } + defer stream.Close() + for { + recv, err := stream.Recv() + if err == io.EOF { + fmt.Println() + return + } + if err != nil { + fmt.Printf("stream chat error: %v\n", err) + return + } + if len(recv.Choices) > 0 { + fmt.Print(recv.Choices[0].Delta.Content.Or("")) + } + } +} + +func systemMsg(content string) chat.ChatCompletionRequestMessage { + return chat.ChatCompletionRequestMessage{ + OneOf: chat.NewChatCompletionRequestSystemMessageChatCompletionRequestMessageSum( + chat.ChatCompletionRequestSystemMessage{ + Role: chat.ChatCompletionRequestSystemMessageRoleSystem, + Content: chat.NewStringChatCompletionMessageContent(content), + }, + ), + } +} + +func userMsg(content string) chat.ChatCompletionRequestMessage { + return chat.ChatCompletionRequestMessage{ + OneOf: chat.NewChatCompletionRequestUserMessageChatCompletionRequestMessageSum( + chat.ChatCompletionRequestUserMessage{ + Role: chat.ChatCompletionRequestUserMessageRoleUser, + Content: chat.NewStringChatCompletionMessageContent(content), + }, + ), + } +} diff --git a/examples/byteplus/chat/function_call/main.go b/examples/byteplus/chat/function_call/main.go new file mode 100644 index 0000000..bcee21e --- /dev/null +++ b/examples/byteplus/chat/function_call/main.go @@ -0,0 +1,140 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "context" + "encoding/json" + "fmt" + "io" + "os" + + "github.com/go-faster/jx" + "github.com/volcengine/ark-runtime-go/arkruntime" + "github.com/volcengine/ark-runtime-go/arkruntime/model/chat" +) + +/** + * Authentication + * If you authorize your endpoint using an API key, you can set your api key to environment variable "ARK_API_KEY" + * client := arkruntime.NewByteplusClientWithApiKey(os.Getenv("ARK_API_KEY")) + */ + +func main() { + client := arkruntime.NewByteplusClientWithApiKey(os.Getenv("ARK_API_KEY")) + ctx := context.Background() + + tools := []chat.ChatCompletionTool{weatherTool()} + req := &chat.ChatCompletionRequest{ + Model: "seed-2-0-lite-260428", + Messages: []chat.ChatCompletionRequestMessage{ + userMsg("What's the weather like in Boston today?"), + }, + Tools: tools, + } + + fmt.Println("----- function call request -----") + resp, err := client.CreateChatCompletion(ctx, req) + if err != nil { + fmt.Printf("standard chat error: %v\n", err) + return + } + out, _ := json.Marshal(resp) + fmt.Println(string(out)) + + fmt.Println("----- function call stream request -----") + stream, err := client.CreateChatCompletionStream(ctx, req) + if err != nil { + fmt.Printf("stream chat error: %v\n", err) + return + } + defer stream.Close() + + type aggCall struct { + ID string + Name string + Args string + } + finalToolCalls := map[int32]*aggCall{} + for { + recv, err := stream.Recv() + if err == io.EOF { + break + } + if err != nil { + fmt.Printf("stream chat error: %v\n", err) + break + } + if len(recv.Choices) == 0 { + continue + } + delta := recv.Choices[0].Delta + fmt.Print(delta.Content.Or("")) + for _, tc := range delta.ToolCalls { + cur, ok := finalToolCalls[tc.Index] + if !ok { + cur = &aggCall{} + finalToolCalls[tc.Index] = cur + } + if id, ok := tc.ID.Get(); ok && cur.ID == "" { + cur.ID = id + } + if fn, ok := tc.Function.Get(); ok { + if name, ok := fn.Name.Get(); ok && cur.Name == "" { + cur.Name = name + } + cur.Args += fn.Arguments.Or("") + } + } + } + for _, c := range finalToolCalls { + fmt.Printf("\ntool_call id=%s name=%s args=%s\n", c.ID, c.Name, c.Args) + } +} + +func userMsg(content string) chat.ChatCompletionRequestMessage { + return chat.ChatCompletionRequestMessage{ + OneOf: chat.NewChatCompletionRequestUserMessageChatCompletionRequestMessageSum( + chat.ChatCompletionRequestUserMessage{ + Role: chat.ChatCompletionRequestUserMessageRoleUser, + Content: chat.NewStringChatCompletionMessageContent(content), + }, + ), + } +} + +// weatherTool builds the get_current_weather tool used in this example. +func weatherTool() chat.ChatCompletionTool { + schema := map[string]any{ + "type": "object", + "properties": map[string]any{ + "location": map[string]any{ + "type": "string", + "description": "The city and state, e.g. San Francisco, CA", + }, + "unit": map[string]any{ + "type": "string", + "enum": []string{"celsius", "fahrenheit"}, + }, + }, + "required": []string{"location"}, + } + return chat.ChatCompletionTool{ + Type: chat.ToolTypeFunction, + Function: chat.FunctionObject{ + Name: "get_current_weather", + Description: chat.NewOptString("Get the current weather in a given location"), + Parameters: chat.NewOptFunctionObjectParameters(toRaw(schema)), + }, + } +} + +func toRaw(m map[string]any) chat.FunctionObjectParameters { + out := make(chat.FunctionObjectParameters, len(m)) + for k, v := range m { + b, _ := json.Marshal(v) + out[k] = jx.Raw(b) + } + return out +} diff --git a/examples/byteplus/chat/reasoning/main.go b/examples/byteplus/chat/reasoning/main.go new file mode 100644 index 0000000..cdd3809 --- /dev/null +++ b/examples/byteplus/chat/reasoning/main.go @@ -0,0 +1,89 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "context" + "fmt" + "io" + "os" + + "github.com/volcengine/ark-runtime-go/arkruntime" + "github.com/volcengine/ark-runtime-go/arkruntime/model/chat" +) + +/** + * Authentication + * If you authorize your endpoint using an API key, you can set your api key to environment variable "ARK_API_KEY" + * client := arkruntime.NewByteplusClientWithApiKey(os.Getenv("ARK_API_KEY")) + */ + +func main() { + client := arkruntime.NewByteplusClientWithApiKey(os.Getenv("ARK_API_KEY")) + ctx := context.Background() + + req := &chat.ChatCompletionRequest{ + Model: "seed-2-0-lite-260428", + Messages: []chat.ChatCompletionRequestMessage{ + userMsg("How many Rs are there in the word 'strawberry'?"), + }, + Thinking: chat.NewOptThinking(chat.Thinking{ + Type: chat.NewOptThinkingMode(chat.ThinkingModeEnabled), + }), + } + + fmt.Println("----- streaming request -----") + stream, err := client.CreateChatCompletionStream(ctx, req) + if err != nil { + fmt.Printf("stream chat error: %v\n", err) + return + } + defer stream.Close() + + for { + recv, err := stream.Recv() + if err == io.EOF { + break + } + if err != nil { + fmt.Printf("stream chat error: %v\n", err) + break + } + if len(recv.Choices) == 0 { + continue + } + delta := recv.Choices[0].Delta + if r, ok := delta.ReasoningContent.Get(); ok && r != "" { + fmt.Print(r) + } else { + fmt.Print(delta.Content.Or("")) + } + } + fmt.Println() + + fmt.Println("----- standard request -----") + resp, err := client.CreateChatCompletion(ctx, req) + if err != nil { + fmt.Printf("standard chat error: %v\n", err) + return + } + if len(resp.Choices) > 0 { + msg := resp.Choices[0].Message + if r, ok := msg.ReasoningContent.Get(); ok && r != "" { + fmt.Println(r) + } + fmt.Println(msg.Content) + } +} + +func userMsg(content string) chat.ChatCompletionRequestMessage { + return chat.ChatCompletionRequestMessage{ + OneOf: chat.NewChatCompletionRequestUserMessageChatCompletionRequestMessageSum( + chat.ChatCompletionRequestUserMessage{ + Role: chat.ChatCompletionRequestUserMessageRoleUser, + Content: chat.NewStringChatCompletionMessageContent(content), + }, + ), + } +} diff --git a/examples/chat/structured_outputs/main.go b/examples/byteplus/chat/structured_outputs/main.go similarity index 97% rename from examples/chat/structured_outputs/main.go rename to examples/byteplus/chat/structured_outputs/main.go index 3305bcc..98d1e35 100644 --- a/examples/chat/structured_outputs/main.go +++ b/examples/byteplus/chat/structured_outputs/main.go @@ -65,7 +65,7 @@ func generateSchema[T any]() chat.ChatCompletionResponseFormatJsonSchemaSchema { var historicalComputerSchema = generateSchema[HistoricalComputer]() func main() { - client := arkruntime.NewClientWithApiKey(os.Getenv("ARK_API_KEY")) + client := arkruntime.NewByteplusClientWithApiKey(os.Getenv("ARK_API_KEY")) ctx := context.Background() question := "What computer ran the first neural network?" @@ -81,7 +81,7 @@ func main() { rf.JSONSchema.SetTo(js) req := &chat.ChatCompletionRequest{ - Model: "${YOUR_ENDPOINT_ID}", + Model: "seed-2-0-pro-260328", Messages: []chat.ChatCompletionRequestMessage{ { OneOf: chat.NewChatCompletionRequestUserMessageChatCompletionRequestMessageSum( diff --git a/examples/byteplus/chat/vision/main.go b/examples/byteplus/chat/vision/main.go new file mode 100644 index 0000000..a0668f0 --- /dev/null +++ b/examples/byteplus/chat/vision/main.go @@ -0,0 +1,67 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "context" + "fmt" + "os" + + "github.com/volcengine/ark-runtime-go/arkruntime" + "github.com/volcengine/ark-runtime-go/arkruntime/model/chat" +) + +/** + * Authentication + * If you authorize your endpoint using an API key, you can set your api key to environment variable "ARK_API_KEY" + * client := arkruntime.NewByteplusClientWithApiKey(os.Getenv("ARK_API_KEY")) + */ + +func main() { + client := arkruntime.NewByteplusClientWithApiKey(os.Getenv("ARK_API_KEY")) + ctx := context.Background() + + textPart := chat.ChatCompletionContentPart{ + OneOf: chat.NewChatCompletionContentPartTextChatCompletionContentPartSum( + chat.ChatCompletionContentPartText{ + Type: chat.ChatCompletionContentPartTextTypeText, + Text: "这是哪里?", + }, + ), + } + imagePart := chat.ChatCompletionContentPart{ + OneOf: chat.NewChatCompletionContentPartImageChatCompletionContentPartSum( + chat.ChatCompletionContentPartImage{ + Type: chat.ChatCompletionContentPartImageTypeImageURL, + ImageURL: chat.ChatCompletionContentPartImageImageUrl{ + URL: chat.NewOptString("https://ark-project.tos-cn-beijing.volces.com/images/view.jpeg"), + }, + }, + ), + } + + req := &chat.ChatCompletionRequest{ + Model: "seed-2-0-lite-260428", + Messages: []chat.ChatCompletionRequestMessage{ + { + OneOf: chat.NewChatCompletionRequestUserMessageChatCompletionRequestMessageSum( + chat.ChatCompletionRequestUserMessage{ + Role: chat.ChatCompletionRequestUserMessageRoleUser, + Content: chat.NewChatCompletionContentPartArrayChatCompletionMessageContent([]chat.ChatCompletionContentPart{textPart, imagePart}), + }, + ), + }, + }, + } + + fmt.Println("----- image input -----") + resp, err := client.CreateChatCompletion(ctx, req) + if err != nil { + fmt.Printf("standard chat error: %v\n", err) + return + } + if len(resp.Choices) > 0 { + fmt.Println(resp.Choices[0].Message.Content) + } +} diff --git a/examples/byteplus/contentgeneration/main.go b/examples/byteplus/contentgeneration/main.go new file mode 100644 index 0000000..b566937 --- /dev/null +++ b/examples/byteplus/contentgeneration/main.go @@ -0,0 +1,149 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "context" + "fmt" + "os" + "time" + + "github.com/volcengine/ark-runtime-go/arkruntime" + "github.com/volcengine/ark-runtime-go/arkruntime/model/contentgeneration" +) + +/** + * Authentication + * If you authorize your endpoint using an API key, set your api key to the + * environment variable "ARK_API_KEY": + * client := arkruntime.NewByteplusClientWithApiKey(os.Getenv("ARK_API_KEY")) + * Note: API keys do not refresh — pick one with no expiration. + */ +func main() { + client := arkruntime.NewByteplusClientWithApiKey(os.Getenv("ARK_API_KEY")) + ctx := context.Background() + modelEp := envOrDefault("SEEDANCE_MODEL", "dreamina-seedance-2-0-fast-260128") + // imageURL := os.Getenv("IMAGE_URL") + + fmt.Println("----- create content generation task -----") + createReq := &contentgeneration.CreateContentGenerationTaskRequest{ + Model: modelEp, + Content: []contentgeneration.ContentItem{ + { + Type: contentgeneration.ContentTypeText, + Text: contentgeneration.NewOptString("龙与地下城女骑士背景是起伏的平原,目光从镜头转向平原"), + }, + /* + { + Type: contentgeneration.ContentTypeImageURL, + ImageURL: contentgeneration.NewOptImageURL(contentgeneration.ImageURL{ + URL: imageURL, + }), + // Role: contentgeneration.NewOptString("first_frame"), + }, + */ + }, + // ServiceTier is not accepted by every model (e.g. seedance 2.0 + // in t2v rejects any explicit tier). Leave it unset by default; + // the "flex" example below shows how to opt-in when a model + // supports it. + ExecutionExpiresAfter: contentgeneration.NewOptInt64(3600), + // CallbackUrl: contentgeneration.NewOptString("CALLBACK_URL"), + } + + createResp, err := client.CreateContentGenerationTask(ctx, createReq) + if err != nil { + fmt.Printf("create content generation error: %v\n", err) + return + } + fmt.Printf("Task Created with ID: %s\n", createResp.ID) + + time.Sleep(1 * time.Minute) + fmt.Println("----- get content generation task -----") + taskID := createResp.ID + + getResp, err := client.GetContentGenerationTask(ctx, taskID) + if err != nil { + fmt.Printf("get content generation task error: %v\n", err) + return + } + + fmt.Printf("Task ID: %s\n", getResp.ID) + fmt.Printf("Model: %s\n", getResp.Model) + fmt.Printf("Status: %s\n", getResp.Status) + if c, ok := getResp.Content.Get(); ok { + if v, ok := c.VideoURL.Get(); ok { + fmt.Printf("Video URL: %s\n", v) + } + } + if u, ok := getResp.Usage.Get(); ok { + fmt.Printf("Completion Tokens: %d\n", u.CompletionTokens) + } + if v, ok := getResp.CreatedAt.Get(); ok { + fmt.Printf("Created At: %d\n", v) + } + if v, ok := getResp.UpdatedAt.Get(); ok { + fmt.Printf("Updated At: %d\n", v) + } + if v, ok := getResp.Seed.Get(); ok { + fmt.Printf("Seed: %d\n", v) + } + if v, ok := getResp.RevisedPrompt.Get(); ok { + fmt.Printf("RevisedPrompt: %s\n", v) + } + if v, ok := getResp.ServiceTier.Get(); ok { + fmt.Printf("ServiceTier: %s\n", v) + } + if v, ok := getResp.ExecutionExpiresAfter.Get(); ok { + fmt.Printf("ExecutionExpiresAfter: %d\n", v) + } + if e, ok := getResp.Error.Get(); ok { + fmt.Printf("Error Code: %s\n", e.Code) + fmt.Printf("Error Message: %s\n", e.Message) + } + + fmt.Println("----- list content generation task -----") + + pageNum := int32(1) + pageSize := int32(10) + status := contentgeneration.TaskStatusSucceeded + tier := "default" + listReq := &arkruntime.ListContentGenerationTasksRequest{ + PageNum: &pageNum, + PageSize: &pageSize, + Status: &status, + ServiceTier: &tier, + // TaskIDs: []string{"cgt-example-1", "cgt-example-2"}, + // Model: &modelEp, + } + listResp, err := client.ListContentGenerationTasks(ctx, listReq) + if err != nil { + fmt.Printf("failed to list content generation tasks: %v\n", err) + } else { + fmt.Printf("ListContentGenerationTasks returned %d results\n", listResp.Total) + for _, item := range listResp.Items { + if v, ok := item.ServiceTier.Get(); ok { + fmt.Printf("List Item ServiceTier: %s\n", v) + } + if v, ok := item.ExecutionExpiresAfter.Get(); ok { + fmt.Printf("List Item ExecutionExpiresAfter: %d\n", v) + } + } + } + + fmt.Println("----- delete content generation task -----") + if err := client.DeleteContentGenerationTask(ctx, taskID); err != nil { + fmt.Printf("delete content generation task error: %v\n", err) + } else { + fmt.Println("successfully deleted task id: ", taskID) + } + +} + +func envOrDefault(name, fallback string) string { + if value := os.Getenv(name); value != "" { + return value + } + return fallback +} diff --git a/examples/byteplus/environments/main.go b/examples/byteplus/environments/main.go new file mode 100644 index 0000000..486e77c --- /dev/null +++ b/examples/byteplus/environments/main.go @@ -0,0 +1,82 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +// Environment lifecycle example — Create → Get → List → Update → Delete. +// +// An Environment is the sandbox (network + filesystem policy) an Agent runs +// inside during a Session. This example uses the cloud environment with +// unrestricted networking; production usage will typically restrict either. +// +// export ARK_API_KEY=... +// go run examples/environments/main.go +package main + +import ( + "context" + "fmt" + "log" + "os" + "time" + + "github.com/volcengine/ark-runtime-go/arkruntime" + "github.com/volcengine/ark-runtime-go/arkruntime/model/environment" +) + +func main() { + apiKey := os.Getenv("ARK_API_KEY") + if apiKey == "" { + log.Fatal("set ARK_API_KEY") + } + + client := arkruntime.NewByteplusClientWithApiKey(apiKey) + ctx := context.Background() + + // 1. Create — cloud + unrestricted network. + name := fmt.Sprintf("example-env-%d", time.Now().UnixNano()) + created, err := client.CreateEnvironment(ctx, &environment.CreateEnvironmentRequest{ + Name: name, + Config: environment.NewOptEnvConfig(environment.EnvConfig{ + Type: environment.EnvConfigTypeCloud, + Networking: environment.NewOptNetworkingConfig(environment.NetworkingConfig{ + Type: environment.NetworkingTypeUnrestricted, + }), + }), + }) + if err != nil { + log.Fatalf("create environment: %v", err) + } + fmt.Printf("created: id=%s name=%s\n", created.ID, created.Name) + + defer func() { + if _, err := client.DeleteEnvironment(context.Background(), created.ID); err != nil { + log.Printf("cleanup delete_environment(%s): %v", created.ID, err) + } else { + fmt.Printf("deleted: id=%s\n", created.ID) + } + }() + + // 2. Get + got, err := client.GetEnvironment(ctx, created.ID) + if err != nil { + log.Fatalf("get environment: %v", err) + } + fmt.Printf("get: id=%s name=%s type=%v\n", got.ID, got.Name, got.Type) + + // 3. List + listed, err := client.ListEnvironments(ctx, &environment.EnvironmentsListParams{ + Limit: environment.NewOptInt32(5), + }) + if err != nil { + log.Fatalf("list environments: %v", err) + } + fmt.Printf("list: %d items, next_page=%q\n", len(listed.Data), listed.NextPage.Value) + + // 4. Update — attach a description. + updated, err := client.UpdateEnvironment(ctx, created.ID, &environment.UpdateEnvironmentRequest{ + Description: environment.NewOptString("updated by ark-runtime-go example"), + }) + if err != nil { + log.Fatalf("update environment: %v", err) + } + fmt.Printf("updated: id=%s description=%q\n", updated.ID, updated.Description.Value) +} diff --git a/examples/byteplus/files/main.go b/examples/byteplus/files/main.go new file mode 100644 index 0000000..36159b7 --- /dev/null +++ b/examples/byteplus/files/main.go @@ -0,0 +1,63 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "context" + "flag" + "fmt" + "log" + "os" + + "github.com/volcengine/ark-runtime-go/arkruntime" + "github.com/volcengine/ark-runtime-go/arkruntime/model/file" +) + +func main() { + target := flag.String("file", "", "path to file to upload") + flag.Parse() + if *target == "" { + log.Fatal("usage: main -file ") + } + + apiKey := os.Getenv("ARK_API_KEY") + if apiKey == "" { + log.Fatal("set ARK_API_KEY") + } + + client := arkruntime.NewByteplusClientWithApiKey(apiKey) + ctx := context.Background() + + f, err := os.Open(*target) + if err != nil { + log.Fatal(err) + } + defer f.Close() + + uploaded, err := client.UploadFile(ctx, &file.FileCreateRequest{ + Purpose: file.PurposeUserData, + }, f) + if err != nil { + log.Fatalf("upload: %v", err) + } + fmt.Printf("uploaded: id=%s status=%s\n", uploaded.ID, uploaded.Status) + + ready, err := client.WaitForFileProcessing(ctx, uploaded.ID, arkruntime.WaitForFileProcessingOptions{}) + if err != nil { + log.Fatalf("wait: %v", err) + } + fmt.Printf("ready: status=%s\n", ready.Status) + + listed, err := client.ListFiles(ctx, &file.FilesListParams{}) + if err != nil { + log.Fatalf("list: %v", err) + } + fmt.Printf("list: %d items, has_more=%v\n", len(listed.Data), listed.HasMore) + + deleted, err := client.DeleteFile(ctx, uploaded.ID) + if err != nil { + log.Fatalf("delete: %v", err) + } + fmt.Printf("deleted: id=%s deleted=%v\n", deleted.ID, deleted.Deleted) +} diff --git a/examples/byteplus/images/main.go b/examples/byteplus/images/main.go new file mode 100644 index 0000000..1a442d1 --- /dev/null +++ b/examples/byteplus/images/main.go @@ -0,0 +1,63 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "context" + "fmt" + "os" + + "github.com/volcengine/ark-runtime-go/arkruntime" + "github.com/volcengine/ark-runtime-go/arkruntime/model/images" +) + +/** + * Authentication + * If you authorize your endpoint using an API key, set your api key to the + * environment variable "ARK_API_KEY": + * client := arkruntime.NewByteplusClientWithApiKey(os.Getenv("ARK_API_KEY")) + * Note: API keys do not refresh — pick one with no expiration. + */ +func main() { + client := arkruntime.NewByteplusClientWithApiKey(os.Getenv("ARK_API_KEY")) + ctx := context.Background() + seedreamModel := envOrDefault("SEEDREAM_MODEL", "dola-seedream-5-0-pro-260628") + + fmt.Println("----- [Seedream] generate images (response format: url) -----") + req := &images.CreateImageGenerationRequest{ + Model: seedreamModel, + Prompt: "龙与地下城女骑士背景是起伏的平原,目光从镜头转向平原", + ResponseFormat: images.NewOptResponseFormat(images.ResponseFormatURL), + Seed: images.NewOptInt64(1234567890), + Watermark: images.NewOptBool(true), + Size: images.NewOptString("1024x1024"), + } + + resp, err := client.GenerateImages(ctx, req) + if err != nil { + fmt.Printf("generate images error: %v\n", err) + return + } + if resp.Error.IsSet() { + fmt.Printf("Error Code: %s\n", resp.Error.Value.Code) + fmt.Printf("Error Message: %s\n", resp.Error.Value.Message) + return + } + fmt.Printf("Model: %s\n", resp.Model) + if len(resp.Data) > 0 { + fmt.Printf("Image URL: %s\n", resp.Data[0].URL.Value) + } + if resp.Usage.IsSet() { + fmt.Printf("Generated Images: %d\n", resp.Usage.Value.GeneratedImages) + } + fmt.Printf("Created: %d\n", resp.Created) + +} + +func envOrDefault(name, fallback string) string { + if value := os.Getenv(name); value != "" { + return value + } + return fallback +} diff --git a/examples/byteplus/listinputitems/main.go b/examples/byteplus/listinputitems/main.go new file mode 100644 index 0000000..e0aeeba --- /dev/null +++ b/examples/byteplus/listinputitems/main.go @@ -0,0 +1,65 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +// List-input-items example: creates a response, then paginates through +// its input items via ListResponseInputItems. +// +// Run with: +// +// ARK_API_KEY=... go run ./listinputitems +package main + +import ( + "context" + "fmt" + "os" + + "github.com/volcengine/ark-runtime-go/arkruntime" + "github.com/volcengine/ark-runtime-go/arkruntime/model/responses" +) + +const defaultLimit int32 = 10 + +func main() { + client := arkruntime.NewByteplusClientWithApiKey(os.Getenv("ARK_API_KEY")) + ctx := context.Background() + + resp, err := client.CreateResponses(ctx, &responses.ResponsesRequest{ + Model: "seed-2-0-lite-260428", + Input: responses.NewStringResponsesInput("hello"), + }) + if err != nil { + fmt.Printf("create error: %v\n", err) + return + } + fmt.Printf("created response: id=%s\n", resp.ID) + + params := &responses.ResponsesListInputItemsParams{ + ResponseId: resp.ID, + Limit: responses.NewOptInt32(defaultLimit), + } + + page, err := client.ListResponseInputItems(ctx, params) + if err != nil { + fmt.Printf("list error: %v\n", err) + return + } + for _, item := range page.Data { + fmt.Printf("%+v\n", item) + } + + hasMore, _ := page.HasMore.Get() + if !hasMore { + return + } + + params.Before = responses.NewOptString(page.FirstID) + page, err = client.ListResponseInputItems(ctx, params) + if err != nil { + fmt.Printf("list error: %v\n", err) + return + } + for _, item := range page.Data { + fmt.Printf("%+v\n", item) + } +} diff --git a/examples/byteplus/memory_stores/main.go b/examples/byteplus/memory_stores/main.go new file mode 100644 index 0000000..7f80167 --- /dev/null +++ b/examples/byteplus/memory_stores/main.go @@ -0,0 +1,105 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +// MemoryStore + Memory lifecycle example. +// +// A MemoryStore is a namespace of Memory documents keyed by path. This example +// covers the full CRUD on both levels: +// +// POST /api/v3/memory_stores (CreateMemoryStore) +// GET /api/v3/memory_stores/:store_id (GetMemoryStore) +// GET /api/v3/memory_stores (ListMemoryStores) +// POST /api/v3/memory_stores/:store_id (UpdateMemoryStore) +// POST /api/v3/memory_stores/:store_id/memories (CreateMemory) +// GET /api/v3/memory_stores/:store_id/memories/:id (GetMemory) +// GET /api/v3/memory_stores/:store_id/memories (ListMemories) +// POST /api/v3/memory_stores/:store_id/memories/:id (UpdateMemory) +// DELETE /api/v3/memory_stores/:store_id/memories/:id (DeleteMemory) +// DELETE /api/v3/memory_stores/:store_id (DeleteMemoryStore) +// +// export ARK_API_KEY=... +// go run examples/memory_stores/main.go +package main + +import ( + "context" + "fmt" + "log" + "os" + "time" + + "github.com/volcengine/ark-runtime-go/arkruntime" + "github.com/volcengine/ark-runtime-go/arkruntime/model/memory" +) + +func main() { + apiKey := os.Getenv("ARK_API_KEY") + if apiKey == "" { + log.Fatal("set ARK_API_KEY") + } + + client := arkruntime.NewByteplusClientWithApiKey(apiKey) + ctx := context.Background() + + // 1. Create a memory store. + storeName := fmt.Sprintf("example-store-%d", time.Now().UnixNano()) + store, err := client.CreateMemoryStore(ctx, &memory.CreateMemoryStoreRequest{ + Name: storeName, + }) + if err != nil { + log.Fatalf("create memory store: %v", err) + } + fmt.Printf("store: id=%s name=%s\n", store.ID, store.Name) + + defer func() { + if _, err := client.DeleteMemoryStore(context.Background(), store.ID); err != nil { + log.Printf("cleanup delete_memory_store(%s): %v", store.ID, err) + } else { + fmt.Printf("store: deleted id=%s\n", store.ID) + } + }() + + // 2. Create a memory doc inside it. + path := fmt.Sprintf("/example/note-%d.md", time.Now().UnixNano()) + mem, err := client.CreateMemory(ctx, store.ID, &memory.CreateMemoryRequest{ + Path: path, + Content: "hello from ark-runtime-go example", + }) + if err != nil { + log.Fatalf("create memory: %v", err) + } + fmt.Printf("memory: id=%s path=%s sha256=%s\n", mem.ID, mem.Path, mem.ContentSHA256) + + // 3. Get + list. + got, err := client.GetMemory(ctx, store.ID, mem.ID) + if err != nil { + log.Fatalf("get memory: %v", err) + } + fmt.Printf("get: id=%s path=%s\n", got.ID, got.Path) + + list, err := client.ListMemories(ctx, store.ID, &memory.MemoriesListParams{ + Limit: memory.NewOptInt32(10), + }) + if err != nil { + log.Fatalf("list memories: %v", err) + } + fmt.Printf("list: %d items in store\n", len(list.Data)) + + // 4. Update — the SHA256 should change after new content. + if _, err := client.UpdateMemory(ctx, store.ID, mem.ID, &memory.UpdateMemoryRequest{ + Content: memory.NewOptString("updated content"), + }); err != nil { + log.Fatalf("update memory: %v", err) + } + got2, err := client.GetMemory(ctx, store.ID, mem.ID) + if err != nil { + log.Fatalf("re-get memory: %v", err) + } + fmt.Printf("updated: id=%s new_sha256=%s (was %s)\n", got2.ID, got2.ContentSHA256, mem.ContentSHA256) + + // 5. Delete memory (store is cleaned up via defer above). + if _, err := client.DeleteMemory(ctx, store.ID, mem.ID); err != nil { + log.Fatalf("delete memory: %v", err) + } + fmt.Printf("memory: deleted id=%s\n", mem.ID) +} diff --git a/examples/byteplus/multimodalembeddings/main.go b/examples/byteplus/multimodalembeddings/main.go new file mode 100644 index 0000000..588f3f6 --- /dev/null +++ b/examples/byteplus/multimodalembeddings/main.go @@ -0,0 +1,49 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "context" + "encoding/json" + "fmt" + "os" + + "github.com/volcengine/ark-runtime-go/arkruntime" + "github.com/volcengine/ark-runtime-go/arkruntime/model/multimodalembedding" +) + +/** + * Authentication + * If you authorize your endpoint using an API key, you can set your api key to environment variable "ARK_API_KEY" + * client := arkruntime.NewByteplusClientWithApiKey(os.Getenv("ARK_API_KEY")) + */ + +func main() { + client := arkruntime.NewByteplusClientWithApiKey( + os.Getenv("ARK_API_KEY"), + ) + ctx := context.Background() + + fmt.Println("----- multimodal embeddings request -----") + req := &multimodalembedding.MultiModalEmbeddingRequest{ + Model: "skylark-embedding-vision-251215", + Input: []multimodalembedding.EmbeddingInput{ + { + Type: multimodalembedding.EmbeddingInputTypeImageURL, + ImageURL: multimodalembedding.NewOptImageURL(multimodalembedding.ImageURL{ + URL: "https://ark-project.tos-cn-beijing.volces.com/images/view.jpeg", + }), + }, + }, + } + + resp, err := client.CreateMultiModalEmbeddings(ctx, req) + if err != nil { + fmt.Printf("multimodal embeddings error: %v\n", err) + return + } + + s, _ := json.Marshal(resp) + fmt.Println(string(s)) +} diff --git a/examples/byteplus/responses/basic/main.go b/examples/byteplus/responses/basic/main.go new file mode 100644 index 0000000..228dccd --- /dev/null +++ b/examples/byteplus/responses/basic/main.go @@ -0,0 +1,107 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "context" + "fmt" + "io" + "os" + + "github.com/volcengine/ark-runtime-go/arkruntime" + "github.com/volcengine/ark-runtime-go/arkruntime/model/responses" +) + +/** + * Authentication + * If you authorize your endpoint using an API key, you can set your api key to environment variable "ARK_API_KEY" + * client := arkruntime.NewByteplusClientWithApiKey(os.Getenv("ARK_API_KEY")) + */ + +func main() { + client := arkruntime.NewByteplusClientWithApiKey(os.Getenv("ARK_API_KEY")) + ctx := context.Background() + + fmt.Println("----- round 1 message -----") + // round 1 message: a single text input item + userMessage := responses.ItemEasyMessage{ + Role: responses.NewOptMessageRole(responses.MessageRoleUser), + Content: responses.NewContentItemArrayMessageContent([]responses.ContentItem{ + { + OneOf: responses.NewContentItemTextContentItemSum(responses.ContentItemText{ + Type: responses.ContentItemTextTypeInputText, + Text: "请介绍一下你自己", + }), + }, + }), + } + inputItem := responses.InputItem{ + OneOf: responses.NewItemEasyMessageInputItemSum(userMessage), + } + + req := &responses.ResponsesRequest{ + Model: "seed-2-0-lite-260428", + Input: responses.NewInputItemArrayResponsesInput([]responses.InputItem{inputItem}), + } + + stream, err := client.CreateResponsesStream(ctx, req) + if err != nil { + fmt.Printf("stream error: %v\n", err) + return + } + var responseID string + for { + event, err := stream.Recv() + if err == io.EOF { + break + } + if err != nil { + fmt.Printf("stream error: %v\n", err) + return + } + handleEvent(event) + if event.OneOf.Type == responses.ResponseCreatedEventResponseStreamEventSum { + responseID = event.OneOf.ResponseCreatedEvent.Response.ID + } + } + + fmt.Println() + // round 2: reference the prior response and pass a plain string input + fmt.Println("----- round 2 -----") + req.Input = responses.NewStringResponsesInput("总结一下我们刚才聊了什么") + req.PreviousResponseID = responses.NewOptString(responseID) + stream, err = client.CreateResponsesStream(ctx, req) + if err != nil { + fmt.Printf("stream error: %v\n", err) + return + } + for { + event, err := stream.Recv() + if err == io.EOF { + break + } + if err != nil { + fmt.Printf("stream error: %v\n", err) + return + } + handleEvent(event) + } + fmt.Println() + fmt.Println(responseID) +} + +func handleEvent(event *responses.ResponseStreamEvent) { + switch event.OneOf.Type { + case responses.ResponseReasoningSummaryTextDeltaEventResponseStreamEventSum: + fmt.Print(event.OneOf.ResponseReasoningSummaryTextDeltaEvent.Delta.Or("")) + case responses.ResponseReasoningSummaryTextDoneEventResponseStreamEventSum: + fmt.Printf("\naggregated reasoning text: %s\n", event.OneOf.ResponseReasoningSummaryTextDoneEvent.Text.Or("")) + case responses.ResponseTextDeltaEventResponseStreamEventSum: + fmt.Print(event.OneOf.ResponseTextDeltaEvent.Delta.Or("")) + case responses.ResponseTextDoneEventResponseStreamEventSum: + fmt.Printf("\naggregated output text: %s\n", event.OneOf.ResponseTextDoneEvent.Text.Or("")) + default: + return + } +} diff --git a/examples/byteplus/responses/function_call/main.go b/examples/byteplus/responses/function_call/main.go new file mode 100644 index 0000000..b2990be --- /dev/null +++ b/examples/byteplus/responses/function_call/main.go @@ -0,0 +1,195 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +// Function-calling example: registers a `sum(a, b)` tool, sends a query +// shaped to trigger it, executes the tool locally when the model emits a +// call, and feeds the result back as a follow-up turn so the model can +// produce its final natural-language answer. +// +// Run with: +// +// ARK_API_KEY=... go run ./examples/responses/function_call +package main + +import ( + "context" + "encoding/json" + "fmt" + "io" + "os" + "strings" + + "github.com/go-faster/jx" + "github.com/volcengine/ark-runtime-go/arkruntime" + "github.com/volcengine/ark-runtime-go/arkruntime/model/responses" +) + +const modelName = "seed-2-0-lite-260428" + +func main() { + client := arkruntime.NewByteplusClientWithApiKey(os.Getenv("ARK_API_KEY")) + ctx := context.Background() + + sumTool := responses.Tool{ + OneOf: responses.NewFunctionToolToolSum(responses.FunctionTool{ + Type: responses.FunctionToolTypeFunction, + Name: "sum", + Description: responses.NewOptString("add two integers and return the sum"), + Parameters: responses.NewOptFunctionToolParameters(responses.FunctionToolParameters{ + "type": jx.Raw(`"object"`), + "properties": jx.Raw(`{ + "a": {"type": "integer", "description": "first addend"}, + "b": {"type": "integer", "description": "second addend"} + }`), + "required": jx.Raw(`["a", "b"]`), + }), + }), + } + + userMsg := responses.ItemEasyMessage{ + Role: responses.NewOptMessageRole(responses.MessageRoleUser), + Content: responses.NewContentItemArrayMessageContent([]responses.ContentItem{ + { + OneOf: responses.NewContentItemTextContentItemSum(responses.ContentItemText{ + Type: responses.ContentItemTextTypeInputText, + Text: "请用 sum 工具帮我计算 1 + 2 等于多少", + }), + }, + }), + } + + req := &responses.ResponsesRequest{ + Model: modelName, + Input: responses.NewInputItemArrayResponsesInput([]responses.InputItem{ + {OneOf: responses.NewItemEasyMessageInputItemSum(userMsg)}, + }), + Tools: []responses.Tool{sumTool}, + } + + fmt.Println("----- round 1: model decides to call the tool -----") + toolCall, err := streamUntilToolCall(ctx, client, req) + if err != nil { + fmt.Printf("round 1 stream error: %v\n", err) + return + } + if toolCall == nil { + fmt.Println("model returned a plain-text answer; tool was not invoked") + return + } + + args := toolCall.Arguments.Or("") + callID := toolCall.CallID.Or("") + fmt.Printf("\n[tool call] name=%s call_id=%s arguments=%s\n", + toolCall.Name.Or(""), callID, args) + + result, err := executeSum(args) + if err != nil { + fmt.Printf("local sum() error: %v\n", err) + return + } + fmt.Printf("[tool result] sum=%s\n", result) + + // Round 2: append the tool's call (the assistant's emission) and the + // tool's output (our local result) to the input list, then re-stream so + // the model can produce its natural-language answer using the result. + toolOutput := responses.ItemFunctionToolCallOutput{ + Type: responses.ItemFunctionToolCallOutputTypeFunctionCallOutput, + CallID: responses.NewOptString(callID), + Output: responses.NewOptMessageContent(responses.NewStringMessageContent(result)), + } + + req.Input = responses.NewInputItemArrayResponsesInput([]responses.InputItem{ + {OneOf: responses.NewItemEasyMessageInputItemSum(userMsg)}, + {OneOf: responses.NewItemFunctionToolCallInputItemSum(*toolCall)}, + {OneOf: responses.NewItemFunctionToolCallOutputInputItemSum(toolOutput)}, + }) + + fmt.Println("\n----- round 2: model produces final answer -----") + if err := streamFinalAnswer(ctx, client, req); err != nil { + fmt.Printf("round 2 stream error: %v\n", err) + return + } + fmt.Println() +} + +// streamUntilToolCall consumes the stream and watches for an +// `output_item.done` event whose item is an ItemFunctionToolCall, returning +// it. Returns nil if the model produced only text and never called a tool. +// Reasoning + text deltas are printed for visibility. +func streamUntilToolCall( + ctx context.Context, + client *arkruntime.Client, + req *responses.ResponsesRequest, +) (*responses.ItemFunctionToolCall, error) { + stream, err := client.CreateResponsesStream(ctx, req) + if err != nil { + return nil, err + } + for { + event, err := stream.Recv() + if err == io.EOF { + return nil, nil + } + if err != nil { + return nil, err + } + switch event.OneOf.Type { + case responses.ResponseReasoningSummaryTextDeltaEventResponseStreamEventSum: + fmt.Print(event.OneOf.ResponseReasoningSummaryTextDeltaEvent.Delta.Or("")) + case responses.ResponseTextDeltaEventResponseStreamEventSum: + fmt.Print(event.OneOf.ResponseTextDeltaEvent.Delta.Or("")) + case responses.ResponseOutputItemDoneEventResponseStreamEventSum: + item := event.OneOf.ResponseOutputItemDoneEvent.Item + if call, ok := item.OneOf.GetItemFunctionToolCall(); ok { + return &call, nil + } + } + } +} + +// streamFinalAnswer prints text/reasoning deltas to stdout until EOF. +func streamFinalAnswer( + ctx context.Context, + client *arkruntime.Client, + req *responses.ResponsesRequest, +) error { + stream, err := client.CreateResponsesStream(ctx, req) + if err != nil { + return err + } + for { + event, err := stream.Recv() + if err == io.EOF { + return nil + } + if err != nil { + return err + } + switch event.OneOf.Type { + case responses.ResponseReasoningSummaryTextDeltaEventResponseStreamEventSum: + fmt.Print(event.OneOf.ResponseReasoningSummaryTextDeltaEvent.Delta.Or("")) + case responses.ResponseTextDeltaEventResponseStreamEventSum: + fmt.Print(event.OneOf.ResponseTextDeltaEvent.Delta.Or("")) + case responses.ResponseTextDoneEventResponseStreamEventSum: + fmt.Printf("\n[final text] %s\n", event.OneOf.ResponseTextDoneEvent.Text.Or("")) + } + } +} + +// executeSum parses the JSON arguments emitted by the model and computes +// the sum locally. Returns the result as a string so it can flow back to +// the model verbatim as a tool output. +func executeSum(jsonArgs string) (string, error) { + jsonArgs = strings.TrimSpace(jsonArgs) + if jsonArgs == "" { + return "", fmt.Errorf("empty arguments") + } + var parsed struct { + A int64 `json:"a"` + B int64 `json:"b"` + } + if err := json.Unmarshal([]byte(jsonArgs), &parsed); err != nil { + return "", fmt.Errorf("parse arguments %q: %w", jsonArgs, err) + } + return fmt.Sprintf("%d", parsed.A+parsed.B), nil +} diff --git a/examples/byteplus/responses/mcp/main.go b/examples/byteplus/responses/mcp/main.go new file mode 100644 index 0000000..11b4850 --- /dev/null +++ b/examples/byteplus/responses/mcp/main.go @@ -0,0 +1,254 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +// MCP tool example: registers a remote MCP server (deepwiki) as a tool, +// walks through the human-in-the-loop approval flow when the model asks +// to call it, and demonstrates skipping approval entirely with +// “RequireApproval = never“. Both streaming and non-streaming variants. +// +// MCP support is currently behind the “ark-beta-mcp: true“ request +// header, which the example sets on every call. +// +// Run with: +// +// ARK_API_KEY=... go run ./examples/responses/mcp +package main + +import ( + "context" + "fmt" + "io" + "os" + + "github.com/volcengine/ark-runtime-go/arkruntime" + "github.com/volcengine/ark-runtime-go/arkruntime/model/responses" +) + +const ( + modelName = "seed-2-0-lite-260428" + mcpServerURL = "https://mcp.deepwiki.com/mcp" + mcpLabel = "deepwiki" + mcpBetaHeader = "ark-beta-mcp" + userPrompt = "查一下 mark3labs/mcp-go 这个仓库的结构" +) + +func main() { + stream() + fmt.Println() + nonStream() +} + +// nonStream demonstrates the three rounds with non-streaming requests. +// +// 1. Send the prompt → model returns an mcp_approval_request. +// 2. Send back an mcp_approval_response with approve=true → model now +// executes the call and returns the answer. +// 3. Re-send the original prompt with RequireApproval=never to skip the +// approval roundtrip entirely. +func nonStream() { + fmt.Println("===== non-streaming =====") + client := arkruntime.NewByteplusClientWithApiKey(os.Getenv("ARK_API_KEY")) + ctx := context.Background() + + // Round 1: ask, model responds with an approval request. + fmt.Println("----- round 1: prompt → approval request -----") + req1 := &responses.ResponsesRequest{ + Model: modelName, + Input: responses.NewInputItemArrayResponsesInput([]responses.InputItem{ + {OneOf: responses.NewItemEasyMessageInputItemSum(buildUserMessage())}, + }), + Tools: []responses.Tool{mcpTool(false)}, + } + resp1, err := client.CreateResponses(ctx, req1, arkruntime.WithCustomHeader(mcpBetaHeader, "true")) + if err != nil { + fmt.Printf("round 1 error: %v\n", err) + return + } + fmt.Printf("response: %+v\n", resp1) + + approvalReqID := findApprovalRequestID(resp1.Output) + if approvalReqID == "" { + fmt.Println("no approval request emitted; nothing to approve") + return + } + responseID := resp1.ID + + // Round 2: approve. + fmt.Println("\n----- round 2: approve -----") + req2 := &responses.ResponsesRequest{ + Model: modelName, + Input: responses.NewInputItemArrayResponsesInput([]responses.InputItem{ + {OneOf: responses.NewItemFunctionMcpApprovalResponseInputItemSum(responses.ItemFunctionMcpApprovalResponse{ + Type: responses.ItemFunctionMcpApprovalResponseTypeMcpApprovalResponse, + ApprovalRequestID: approvalReqID, + Approve: true, + })}, + }), + Tools: []responses.Tool{mcpTool(false)}, + PreviousResponseID: responses.NewOptString(responseID), + } + resp2, err := client.CreateResponses(ctx, req2, arkruntime.WithCustomHeader(mcpBetaHeader, "true")) + if err != nil { + fmt.Printf("round 2 error: %v\n", err) + return + } + fmt.Printf("response: %+v\n", resp2) + + // Round 3: never require approval. + fmt.Println("\n----- round 3: skip approval entirely -----") + req3 := &responses.ResponsesRequest{ + Model: modelName, + Input: responses.NewInputItemArrayResponsesInput([]responses.InputItem{ + {OneOf: responses.NewItemEasyMessageInputItemSum(buildUserMessage())}, + }), + Tools: []responses.Tool{mcpTool(true)}, + } + resp3, err := client.CreateResponses(ctx, req3, arkruntime.WithCustomHeader(mcpBetaHeader, "true")) + if err != nil { + fmt.Printf("round 3 error: %v\n", err) + return + } + fmt.Printf("response: %+v\n", resp3) +} + +// stream mirrors nonStream but consumes the SSE stream. The approval +// request id is captured from output_item.done events instead of the +// final response object. +func stream() { + fmt.Println("===== streaming =====") + client := arkruntime.NewByteplusClientWithApiKey(os.Getenv("ARK_API_KEY")) + ctx := context.Background() + + fmt.Println("----- round 1: prompt → approval request (streaming) -----") + req1 := &responses.ResponsesRequest{ + Model: modelName, + Input: responses.NewInputItemArrayResponsesInput([]responses.InputItem{ + {OneOf: responses.NewItemEasyMessageInputItemSum(buildUserMessage())}, + }), + Tools: []responses.Tool{mcpTool(false)}, + } + responseID, approvalReqID, err := streamAndCapture(ctx, client, req1) + if err != nil { + fmt.Printf("round 1 stream error: %v\n", err) + return + } + if approvalReqID == "" { + fmt.Println("no approval request emitted; nothing to approve") + return + } + + fmt.Println("\n----- round 2: approve (streaming) -----") + req2 := &responses.ResponsesRequest{ + Model: modelName, + Input: responses.NewInputItemArrayResponsesInput([]responses.InputItem{ + {OneOf: responses.NewItemFunctionMcpApprovalResponseInputItemSum(responses.ItemFunctionMcpApprovalResponse{ + Type: responses.ItemFunctionMcpApprovalResponseTypeMcpApprovalResponse, + ApprovalRequestID: approvalReqID, + Approve: true, + })}, + }), + Tools: []responses.Tool{mcpTool(false)}, + PreviousResponseID: responses.NewOptString(responseID), + } + if _, _, err := streamAndCapture(ctx, client, req2); err != nil { + fmt.Printf("round 2 stream error: %v\n", err) + return + } + + fmt.Println("\n----- round 3: skip approval entirely (streaming) -----") + req3 := &responses.ResponsesRequest{ + Model: modelName, + Input: responses.NewInputItemArrayResponsesInput([]responses.InputItem{ + {OneOf: responses.NewItemEasyMessageInputItemSum(buildUserMessage())}, + }), + Tools: []responses.Tool{mcpTool(true)}, + } + if _, _, err := streamAndCapture(ctx, client, req3); err != nil { + fmt.Printf("round 3 stream error: %v\n", err) + } +} + +func buildUserMessage() responses.ItemEasyMessage { + return responses.ItemEasyMessage{ + Role: responses.NewOptMessageRole(responses.MessageRoleUser), + Content: responses.NewContentItemArrayMessageContent([]responses.ContentItem{ + { + OneOf: responses.NewContentItemTextContentItemSum(responses.ContentItemText{ + Type: responses.ContentItemTextTypeInputText, + Text: userPrompt, + }), + }, + }), + } +} + +// mcpTool builds the deepwiki MCP tool. When neverApprove is true, the +// model is told to skip the approval roundtrip and call directly. +func mcpTool(neverApprove bool) responses.Tool { + mcp := responses.McpTool{ + Type: responses.McpToolTypeMcp, + ServerLabel: mcpLabel, + ServerURL: mcpServerURL, + ServerDescription: responses.NewOptString("test desc"), + } + if neverApprove { + mcp.RequireApproval = responses.NewOptMcpRequireApproval( + responses.NewMcpApprovalModeMcpRequireApproval(responses.McpApprovalModeNever), + ) + } + return responses.Tool{OneOf: responses.NewMcpToolToolSum(mcp)} +} + +// findApprovalRequestID scans the model's Output items for an +// ItemFunctionMcpApprovalRequest and returns its id (empty if none). +func findApprovalRequestID(output []responses.OutputItem) string { + for i := len(output) - 1; i >= 0; i-- { + if req, ok := output[i].OneOf.GetItemFunctionMcpApprovalRequest(); ok { + return req.ID.Or("") + } + } + return "" +} + +// streamAndCapture prints reasoning + text deltas, surfaces MCP-specific +// progress events for visibility, and returns (responseID, +// approvalRequestID) so the caller can chain follow-up rounds. +func streamAndCapture( + ctx context.Context, + client *arkruntime.Client, + req *responses.ResponsesRequest, +) (string, string, error) { + stream, err := client.CreateResponsesStream(ctx, req, arkruntime.WithCustomHeader(mcpBetaHeader, "true")) + if err != nil { + return "", "", err + } + var responseID, approvalReqID string + for { + event, err := stream.Recv() + if err == io.EOF { + return responseID, approvalReqID, nil + } + if err != nil { + return responseID, approvalReqID, err + } + switch event.OneOf.Type { + case responses.ResponseCreatedEventResponseStreamEventSum: + responseID = event.OneOf.ResponseCreatedEvent.Response.ID + case responses.ResponseReasoningSummaryTextDeltaEventResponseStreamEventSum: + fmt.Print(event.OneOf.ResponseReasoningSummaryTextDeltaEvent.Delta.Or("")) + case responses.ResponseTextDeltaEventResponseStreamEventSum: + fmt.Print(event.OneOf.ResponseTextDeltaEvent.Delta.Or("")) + case responses.ResponseTextDoneEventResponseStreamEventSum: + fmt.Printf("\n[text done] %s\n", event.OneOf.ResponseTextDoneEvent.Text.Or("")) + case responses.ResponseOutputItemDoneEventResponseStreamEventSum: + item := event.OneOf.ResponseOutputItemDoneEvent.Item + if req, ok := item.OneOf.GetItemFunctionMcpApprovalRequest(); ok { + if id := req.ID.Or(""); id != "" { + approvalReqID = id + } + fmt.Printf("\n[mcp approval request] name=%s arguments=%s\n", + req.Name, req.Arguments) + } + } + } +} diff --git a/examples/responses/video/main.go b/examples/byteplus/responses/video/main.go similarity index 97% rename from examples/responses/video/main.go rename to examples/byteplus/responses/video/main.go index 7d11c5c..212a660 100644 --- a/examples/responses/video/main.go +++ b/examples/byteplus/responses/video/main.go @@ -25,10 +25,10 @@ import ( "github.com/volcengine/ark-runtime-go/arkruntime/model/responses" ) -const modelName = "${YOUR_ENDPOINT_ID}" +const modelName = "seed-2-0-lite-260428" func main() { - client := arkruntime.NewClientWithApiKey(os.Getenv("ARK_API_KEY")) + client := arkruntime.NewByteplusClientWithApiKey(os.Getenv("ARK_API_KEY")) ctx := context.Background() fileID, err := uploadAndAwait(ctx, client, "ark_vlm_video_input.mp4") diff --git a/examples/byteplus/sessions_loop/main.go b/examples/byteplus/sessions_loop/main.go new file mode 100644 index 0000000..9bc10ce --- /dev/null +++ b/examples/byteplus/sessions_loop/main.go @@ -0,0 +1,151 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +// End-to-end agent-loop example — Create Agent + Environment + Session, +// send a text prompt, stream events until the loop settles (status_idle / +// terminated / error), and print the assistant's response. +// +// This is the smallest full agent-loop demo — it exercises: +// +// POST /api/v3/agents (CreateAgent) +// POST /api/v3/environments (CreateEnvironment) +// POST /api/v3/sessions (CreateSession) +// POST /api/v3/sessions/:id/events (SendSessionEvents — user.message) +// GET /api/v3/sessions/:id/events (stream) (StreamSessionEvents — SSE) +// +// export ARK_API_KEY=... +// export ARK_MODEL_ID=seed-2-0-lite-260428 +// go run examples/sessions_loop/main.go +package main + +import ( + "context" + "fmt" + "log" + "os" + "strings" + "time" + + "github.com/volcengine/ark-runtime-go/arkruntime" + "github.com/volcengine/ark-runtime-go/arkruntime/model/agent" + "github.com/volcengine/ark-runtime-go/arkruntime/model/environment" + "github.com/volcengine/ark-runtime-go/arkruntime/model/session" +) + +func main() { + apiKey := os.Getenv("ARK_API_KEY") + if apiKey == "" { + log.Fatal("set ARK_API_KEY") + } + modelID := os.Getenv("ARK_MODEL_ID") + if modelID == "" { + modelID = "${YOUR_MODEL_ID}" + } + + client := arkruntime.NewByteplusClientWithApiKey(apiKey) + ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second) + defer cancel() + + // 1. Agent — general-purpose with the toolset that ships with managed agents. + ag, err := client.CreateAgent(ctx, &agent.CreateAgentRequest{ + Name: fmt.Sprintf("example-loop-agent-%d", time.Now().UnixNano()), + Model: agent.ModelConfig{ID: modelID}, + System: agent.NewOptString( + "You are a helpful assistant. Answer the user's question briefly."), + Tools: []agent.ToolItem{{Type: "agent_toolset_20260401"}}, + }) + if err != nil { + log.Fatalf("create agent: %v", err) + } + defer func() { _, _ = client.DeleteAgent(context.Background(), ag.ID) }() + fmt.Printf("agent: id=%s\n", ag.ID) + + // 2. Environment — cloud + unrestricted networking. + env, err := client.CreateEnvironment(ctx, &environment.CreateEnvironmentRequest{ + Name: fmt.Sprintf("example-loop-env-%d", time.Now().UnixNano()), + Config: environment.NewOptEnvConfig(environment.EnvConfig{ + Type: environment.EnvConfigTypeCloud, + Networking: environment.NewOptNetworkingConfig(environment.NetworkingConfig{ + Type: environment.NetworkingTypeUnrestricted, + }), + }), + }) + if err != nil { + log.Fatalf("create environment: %v", err) + } + defer func() { _, _ = client.DeleteEnvironment(context.Background(), env.ID) }() + fmt.Printf("env: id=%s\n", env.ID) + + // 3. Session — binds the agent to the environment. + sess, err := client.CreateSession(ctx, &session.CreateSessionRequest{ + Agent: session.NewStringAgentIdentifier(ag.ID), + EnvironmentID: env.ID, + Title: session.NewOptString("ark-runtime-go example loop"), + }) + if err != nil { + log.Fatalf("create session: %v", err) + } + defer func() { _, _ = client.DeleteSession(context.Background(), sess.ID) }() + fmt.Printf("session: id=%s\n\n", sess.ID) + + // 4. Open the SSE stream first, then send the user message so we don't + // race and miss the earliest events. + dec, err := client.StreamSessionEvents(ctx, sess.ID) + if err != nil { + log.Fatalf("open stream: %v", err) + } + defer dec.Close() + + // Fire the user.message asynchronously; the stream will surface both + // the echoed user.message and the assistant's agent.message frames. + go func() { + // Small warmup so the SSE reader is fully attached before we push. + time.Sleep(500 * time.Millisecond) + if _, err := client.SendSessionEvents(ctx, sess.ID, &session.SendSessionEventsRequest{ + Events: []session.ManagedAgentsEventParams{{ + OneOf: session.NewManagedAgentsUserMessageEventParamsManagedAgentsEventParamsSum( + session.ManagedAgentsUserMessageEventParams{ + Content: []session.ManagedAgentsMessageContentBlock{{ + OneOf: session.NewManagedAgentsTextBlockManagedAgentsMessageContentBlockSum( + session.ManagedAgentsTextBlock{ + Text: "What's the tallest mountain? One sentence.", + }), + }}, + }), + }}, + }); err != nil { + log.Printf("send user.message: %v", err) + } + }() + + // 5. Drain the stream until the loop settles. session.status_idle is the + // normal terminal event; terminated/error are the failure modes. Every + // frame is delivered as a concrete typed struct — dispatch via a Go + // type-switch instead of hand-parsing JSON. + var assistantOut strings.Builder + done := false + for !done && dec.Next() { + frame := dec.Event() + fmt.Printf("[EVT] %s\n", frame.Type) + + switch ev := frame.Data.(type) { + case *session.ManagedAgentsAgentMessageEvent: + for _, block := range ev.Content { + if block.Text != "" { + assistantOut.WriteString(block.Text) + } + } + case *session.ManagedAgentsSessionStatusIdleEvent, + *session.ManagedAgentsSessionStatusTerminatedEvent, + *session.ManagedAgentsSessionErrorEvent: + _ = ev // terminal — see printed [EVT] type above + done = true + } + } + + if s := strings.TrimSpace(assistantOut.String()); s != "" { + fmt.Printf("\nassistant → %s\n", s) + } else { + fmt.Println("\n(no assistant text captured — check the [EVT] trace above)") + } +} diff --git a/examples/byteplus/sparseembeddings/main.go b/examples/byteplus/sparseembeddings/main.go new file mode 100644 index 0000000..a25e6b1 --- /dev/null +++ b/examples/byteplus/sparseembeddings/main.go @@ -0,0 +1,50 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "context" + "encoding/json" + "fmt" + "os" + + "github.com/volcengine/ark-runtime-go/arkruntime" + "github.com/volcengine/ark-runtime-go/arkruntime/model/multimodalembedding" +) + +/** + * Authentication + * If you authorize your endpoint using an API key, you can set your api key to environment variable "ARK_API_KEY" + * client := arkruntime.NewByteplusClientWithApiKey(os.Getenv("ARK_API_KEY")) + */ + +func main() { + client := arkruntime.NewByteplusClientWithApiKey( + os.Getenv("ARK_API_KEY"), + ) + ctx := context.Background() + + fmt.Println("----- sparse embeddings request -----") + req := &multimodalembedding.MultiModalEmbeddingRequest{ + Model: "skylark-embedding-vision-251215", + Input: []multimodalembedding.EmbeddingInput{ + { + Type: multimodalembedding.EmbeddingInputTypeText, + Text: multimodalembedding.NewOptString("花椰菜又称菜花、花菜,是一种常见的蔬菜。"), + }, + }, + SparseEmbedding: multimodalembedding.NewOptSparseEmbeddingConfig(multimodalembedding.SparseEmbeddingConfig{ + Type: multimodalembedding.NewOptSparseEmbeddingMode(multimodalembedding.SparseEmbeddingModeEnabled), + }), + } + + resp, err := client.CreateMultiModalEmbeddings(ctx, req) + if err != nil { + fmt.Printf("sparse embeddings error: %v\n", err) + return + } + + s, _ := json.Marshal(resp.Data) + fmt.Println(string(s)) +} diff --git a/examples/byteplus/tokenization/main.go b/examples/byteplus/tokenization/main.go new file mode 100644 index 0000000..9e24f03 --- /dev/null +++ b/examples/byteplus/tokenization/main.go @@ -0,0 +1,44 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "context" + "encoding/json" + "fmt" + "os" + + "github.com/volcengine/ark-runtime-go/arkruntime" + "github.com/volcengine/ark-runtime-go/arkruntime/model/tokenization" +) + +/** + * Authentication + * If you authorize your endpoint using an API key, you can set your api key to environment variable "ARK_API_KEY" + * client := arkruntime.NewByteplusClientWithApiKey(os.Getenv("ARK_API_KEY")) + */ + +func main() { + client := arkruntime.NewByteplusClientWithApiKey( + os.Getenv("ARK_API_KEY"), + ) + ctx := context.Background() + + fmt.Println("----- tokenization request -----") + req := &tokenization.TokenizationRequest{ + Model: "${YOUR_ENDPOINT_ID}", + Text: tokenization.NewStringArrayTokenizationInput([]string{ + "花椰菜又称菜花、花菜,是一种常见的蔬菜。", + }), + } + + resp, err := client.CreateTokenization(ctx, req) + if err != nil { + fmt.Printf("tokenization error: %v\n", err) + return + } + + s, _ := json.Marshal(resp) + fmt.Println(string(s)) +} diff --git a/examples/images/main.go b/examples/images/main.go deleted file mode 100644 index 8cbc907..0000000 --- a/examples/images/main.go +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. -// SPDX-License-Identifier: Apache-2.0 - -package main - -import ( - "context" - "fmt" - "os" - - "github.com/volcengine/ark-runtime-go/arkruntime" - "github.com/volcengine/ark-runtime-go/arkruntime/model/images" -) - -/** - * Authentication - * If you authorize your endpoint using an API key, set your api key to the - * environment variable "ARK_API_KEY": - * client := arkruntime.NewClientWithApiKey(os.Getenv("ARK_API_KEY")) - * Note: API keys do not refresh — pick one with no expiration. - */ -func main() { - client := arkruntime.NewClientWithApiKey(os.Getenv("ARK_API_KEY")) - ctx := context.Background() - modelEp := os.Getenv("ENDPOINT_ID") - - fmt.Println("----- [Seedream] generate images (response format: url) -----") - req := &images.CreateImageGenerationRequest{ - Model: modelEp, - Prompt: "龙与地下城女骑士背景是起伏的平原,目光从镜头转向平原", - ResponseFormat: images.NewOptResponseFormat(images.ResponseFormatURL), - Seed: images.NewOptInt64(1234567890), - Watermark: images.NewOptBool(true), - Size: images.NewOptString("1024x1024"), - } - - resp, err := client.GenerateImages(ctx, req) - if err != nil { - fmt.Printf("generate images error: %v\n", err) - return - } - if resp.Error.IsSet() { - fmt.Printf("Error Code: %s\n", resp.Error.Value.Code) - fmt.Printf("Error Message: %s\n", resp.Error.Value.Message) - return - } - fmt.Printf("Model: %s\n", resp.Model) - if len(resp.Data) > 0 { - fmt.Printf("Image URL: %s\n", resp.Data[0].URL.Value) - } - if resp.Usage.IsSet() { - fmt.Printf("Generated Images: %d\n", resp.Usage.Value.GeneratedImages) - } - fmt.Printf("Created: %d\n", resp.Created) - - fmt.Println("----- [Seededit] generate images (with input image) -----") - editReq := &images.CreateImageGenerationRequest{ - Model: modelEp, - Prompt: "把背景换成黄昏的沙漠", - Image: []string{"YOUR_IMAGE_URL_HERE"}, - ResponseFormat: images.NewOptResponseFormat(images.ResponseFormatURL), - Seed: images.NewOptInt64(1234567890), - Watermark: images.NewOptBool(true), - Size: images.NewOptString("adaptive"), - } - if resp, err = client.GenerateImages(ctx, editReq); err != nil { - fmt.Printf("generate images (edit) error: %v\n", err) - return - } - if resp.Error.IsSet() { - fmt.Printf("Error: %s — %s\n", resp.Error.Value.Code, resp.Error.Value.Message) - return - } - if len(resp.Data) > 0 { - fmt.Printf("Edited Image URL: %s\n", resp.Data[0].URL.Value) - } - - fmt.Println("----- [Seedream] sequential image generation -----") - seqReq := &images.CreateImageGenerationRequest{ - Model: modelEp, - Prompt: "星球大战, 场面壮观, 需要描述3个连续场面", - ResponseFormat: images.NewOptResponseFormat(images.ResponseFormatURL), - Seed: images.NewOptInt64(1234567890), - Watermark: images.NewOptBool(true), - Size: images.NewOptString("1024x1024"), - SequentialImageGeneration: images.NewOptSequentialImageGenerationMode(images.SequentialImageGenerationModeAuto), - SequentialImageGenerationOptions: images.NewOptSequentialImageGenerationOptions( - images.SequentialImageGenerationOptions{ - MaxImages: images.NewOptInt32(3), - }, - ), - } - if resp, err = client.GenerateImages(ctx, seqReq); err != nil { - fmt.Printf("generate images (sequential) error: %v\n", err) - return - } - for i, item := range resp.Data { - fmt.Printf("[%d] size=%s url=%s\n", i, item.Size.Value, item.URL.Value) - } -} diff --git a/examples/agents/main.go b/examples/volc/agents/main.go similarity index 98% rename from examples/agents/main.go rename to examples/volc/agents/main.go index fb0b2b5..fef88fe 100644 --- a/examples/agents/main.go +++ b/examples/volc/agents/main.go @@ -31,7 +31,7 @@ func main() { modelID = "${YOUR_MODEL_ID}" } - client := arkruntime.NewClientWithApiKey(apiKey) + client := arkruntime.NewVolcClientWithApiKey(apiKey) ctx := context.Background() // 1. Create diff --git a/examples/batch_chat/main.go b/examples/volc/batch_chat/main.go similarity index 96% rename from examples/batch_chat/main.go rename to examples/volc/batch_chat/main.go index 69f37a6..2a37e49 100644 --- a/examples/batch_chat/main.go +++ b/examples/volc/batch_chat/main.go @@ -18,7 +18,7 @@ import ( /** * Authentication * If you authorize your endpoint using an API key, you can set your api key to environment variable "ARK_API_KEY" - * client := arkruntime.NewClientWithApiKey(os.Getenv("ARK_API_KEY")) + * client := arkruntime.NewVolcClientWithApiKey(os.Getenv("ARK_API_KEY")) * * Batch chat completions use the same request/response shapes as * /chat/completions, but are dispatched against /batch/chat/completions @@ -27,7 +27,7 @@ import ( */ func main() { - client := arkruntime.NewClientWithApiKey( + client := arkruntime.NewVolcClientWithApiKey( os.Getenv("ARK_API_KEY"), arkruntime.WithBatchMaxParallel(3000), // max parallel in-flight batch requests ) diff --git a/examples/batch_embeddings/main.go b/examples/volc/batch_embeddings/main.go similarity index 93% rename from examples/batch_embeddings/main.go rename to examples/volc/batch_embeddings/main.go index fb714cf..9859c5f 100644 --- a/examples/batch_embeddings/main.go +++ b/examples/volc/batch_embeddings/main.go @@ -18,7 +18,7 @@ import ( /** * Authentication * If you authorize your endpoint using an API key, you can set your api key to environment variable "ARK_API_KEY" - * client := arkruntime.NewClientWithApiKey(os.Getenv("ARK_API_KEY")) + * client := arkruntime.NewVolcClientWithApiKey(os.Getenv("ARK_API_KEY")) * * Batch embeddings use the same request/response shapes as /embeddings, * but are dispatched against /batch/embeddings with a high-concurrency @@ -26,7 +26,7 @@ import ( */ func main() { - client := arkruntime.NewClientWithApiKey( + client := arkruntime.NewVolcClientWithApiKey( os.Getenv("ARK_API_KEY"), arkruntime.WithBatchMaxParallel(3000), ) diff --git a/examples/batch_multimodalembeddings/main.go b/examples/volc/batch_multimodalembeddings/main.go similarity index 91% rename from examples/batch_multimodalembeddings/main.go rename to examples/volc/batch_multimodalembeddings/main.go index 2e06966..629515f 100644 --- a/examples/batch_multimodalembeddings/main.go +++ b/examples/volc/batch_multimodalembeddings/main.go @@ -18,7 +18,7 @@ import ( /** * Authentication * If you authorize your endpoint using an API key, you can set your api key to environment variable "ARK_API_KEY" - * client := arkruntime.NewClientWithApiKey(os.Getenv("ARK_API_KEY")) + * client := arkruntime.NewVolcClientWithApiKey(os.Getenv("ARK_API_KEY")) * * Batch multimodal embeddings use the same request/response shapes as * /embeddings/multimodal, but are dispatched against @@ -27,13 +27,13 @@ import ( */ func main() { - client := arkruntime.NewClientWithApiKey( + client := arkruntime.NewVolcClientWithApiKey( os.Getenv("ARK_API_KEY"), arkruntime.WithBatchMaxParallel(3000), ) const total = 20 - requests := mockRequests("doubao-embedding-vision-250615", total) + requests := mockRequests("doubao-embedding-vision-251215", total) ctx, cancel := context.WithTimeout(context.Background(), 24*time.Hour) defer cancel() diff --git a/examples/chat/basic/main.go b/examples/volc/chat/basic/main.go similarity index 92% rename from examples/chat/basic/main.go rename to examples/volc/chat/basic/main.go index 2a6d0fb..236eb8a 100644 --- a/examples/chat/basic/main.go +++ b/examples/volc/chat/basic/main.go @@ -16,11 +16,11 @@ import ( /** * Authentication * If you authorize your endpoint using an API key, you can set your api key to environment variable "ARK_API_KEY" - * client := arkruntime.NewClientWithApiKey(os.Getenv("ARK_API_KEY")) + * client := arkruntime.NewVolcClientWithApiKey(os.Getenv("ARK_API_KEY")) */ func main() { - client := arkruntime.NewClientWithApiKey(os.Getenv("ARK_API_KEY")) + client := arkruntime.NewVolcClientWithApiKey(os.Getenv("ARK_API_KEY")) ctx := context.Background() messages := []chat.ChatCompletionRequestMessage{ @@ -30,7 +30,7 @@ func main() { fmt.Println("----- standard request -----") req := &chat.ChatCompletionRequest{ - Model: "${YOUR_ENDPOINT_ID}", + Model: "doubao-seed-2-1-pro-260628", Messages: messages, } resp, err := client.CreateChatCompletion(ctx, req) diff --git a/examples/chat/function_call/main.go b/examples/volc/chat/function_call/main.go similarity index 94% rename from examples/chat/function_call/main.go rename to examples/volc/chat/function_call/main.go index 4148d01..d0f4de1 100644 --- a/examples/chat/function_call/main.go +++ b/examples/volc/chat/function_call/main.go @@ -18,16 +18,16 @@ import ( /** * Authentication * If you authorize your endpoint using an API key, you can set your api key to environment variable "ARK_API_KEY" - * client := arkruntime.NewClientWithApiKey(os.Getenv("ARK_API_KEY")) + * client := arkruntime.NewVolcClientWithApiKey(os.Getenv("ARK_API_KEY")) */ func main() { - client := arkruntime.NewClientWithApiKey(os.Getenv("ARK_API_KEY")) + client := arkruntime.NewVolcClientWithApiKey(os.Getenv("ARK_API_KEY")) ctx := context.Background() tools := []chat.ChatCompletionTool{weatherTool()} req := &chat.ChatCompletionRequest{ - Model: "${YOUR_ENDPOINT_ID}", + Model: "doubao-seed-2-1-pro-260628", Messages: []chat.ChatCompletionRequestMessage{ userMsg("What's the weather like in Boston today?"), }, diff --git a/examples/chat/reasoning/main.go b/examples/volc/chat/reasoning/main.go similarity index 91% rename from examples/chat/reasoning/main.go rename to examples/volc/chat/reasoning/main.go index 38c4165..3888f6f 100644 --- a/examples/chat/reasoning/main.go +++ b/examples/volc/chat/reasoning/main.go @@ -16,15 +16,15 @@ import ( /** * Authentication * If you authorize your endpoint using an API key, you can set your api key to environment variable "ARK_API_KEY" - * client := arkruntime.NewClientWithApiKey(os.Getenv("ARK_API_KEY")) + * client := arkruntime.NewVolcClientWithApiKey(os.Getenv("ARK_API_KEY")) */ func main() { - client := arkruntime.NewClientWithApiKey(os.Getenv("ARK_API_KEY")) + client := arkruntime.NewVolcClientWithApiKey(os.Getenv("ARK_API_KEY")) ctx := context.Background() req := &chat.ChatCompletionRequest{ - Model: "${YOUR_ENDPOINT_ID}", + Model: "doubao-seed-2-1-pro-260628", Messages: []chat.ChatCompletionRequestMessage{ userMsg("How many Rs are there in the word 'strawberry'?"), }, diff --git a/examples/volc/chat/structured_outputs/main.go b/examples/volc/chat/structured_outputs/main.go new file mode 100644 index 0000000..ba08523 --- /dev/null +++ b/examples/volc/chat/structured_outputs/main.go @@ -0,0 +1,122 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +// Structured outputs example: drives chat.completions with a +// response_format=json_schema, then unmarshals the model's response +// straight into a typed Go struct. +// +// Run with: +// +// ARK_API_KEY=... go run ./examples/chat/structured_outputs +package main + +import ( + "context" + "encoding/json" + "fmt" + "os" + + "github.com/go-faster/jx" + "github.com/invopop/jsonschema" // requires go1.18+ + + "github.com/volcengine/ark-runtime-go/arkruntime" + "github.com/volcengine/ark-runtime-go/arkruntime/model/chat" +) + +// HistoricalComputer is the typed schema we want the model to produce. +type HistoricalComputer struct { + Origin Origin `json:"origin" jsonschema_description:"The origin of the computer"` + Name string `json:"full_name" jsonschema_description:"The name of the device model"` + Legacy string `json:"legacy" jsonschema:"enum=positive,enum=neutral,enum=negative" jsonschema_description:"Its influence on the field of computing"` + NotableFacts []string `json:"notable_facts" jsonschema_description:"A few key facts about the computer"` +} + +type Origin struct { + YearBuilt int64 `json:"year_of_construction" jsonschema_description:"The year it was made"` + Organization string `json:"organization" jsonschema_description:"The organization that was in charge of its development"` +} + +// generateSchema reflects T into a JSON Schema, then renders it to the new +// SDK's ChatCompletionResponseFormatJsonSchemaSchema (which is +// map[string]jx.Raw) by round-tripping through encoding/json. +func generateSchema[T any]() chat.ChatCompletionResponseFormatJsonSchemaSchema { + reflector := jsonschema.Reflector{ + AllowAdditionalProperties: false, + DoNotReference: true, + } + var v T + schema := reflector.Reflect(v) + + raw, err := json.Marshal(schema) + if err != nil { + panic(err) + } + var m map[string]json.RawMessage + if err := json.Unmarshal(raw, &m); err != nil { + panic(err) + } + out := chat.ChatCompletionResponseFormatJsonSchemaSchema{} + for k, v := range m { + out[k] = jx.Raw(v) + } + return out +} + +var historicalComputerSchema = generateSchema[HistoricalComputer]() + +func main() { + client := arkruntime.NewVolcClientWithApiKey(os.Getenv("ARK_API_KEY")) + ctx := context.Background() + + question := "What computer ran the first neural network?" + fmt.Printf("> %s\n", question) + + js := chat.ChatCompletionResponseFormatJsonSchema{Name: "biography"} + js.Description.SetTo("Notable information about a person") + js.Schema.SetTo(historicalComputerSchema) + js.Strict.SetTo(true) + + rf := chat.ChatCompletionResponseFormat{} + rf.Type.SetTo(chat.ResponseFormatTypeJSONSchema) + rf.JSONSchema.SetTo(js) + + req := &chat.ChatCompletionRequest{ + Model: "doubao-seed-2-1-pro-260628", + Messages: []chat.ChatCompletionRequestMessage{ + { + OneOf: chat.NewChatCompletionRequestUserMessageChatCompletionRequestMessageSum( + chat.ChatCompletionRequestUserMessage{ + Role: chat.ChatCompletionRequestUserMessageRoleUser, + Content: chat.NewStringChatCompletionMessageContent(question), + }, + ), + }, + }, + } + req.ResponseFormat.SetTo(rf) + + resp, err := client.CreateChatCompletion(ctx, req) + if err != nil { + fmt.Printf("structured output chat error: %v\n", err) + return + } + if len(resp.Choices) == 0 { + fmt.Println("no choices in response") + return + } + + // The model responds with a JSON string; parse it into our typed struct. + var computer HistoricalComputer + if err := json.Unmarshal([]byte(resp.Choices[0].Message.Content), &computer); err != nil { + panic(err) + } + + fmt.Printf("Name: %v\n", computer.Name) + fmt.Printf("Year: %v\n", computer.Origin.YearBuilt) + fmt.Printf("Org: %v\n", computer.Origin.Organization) + fmt.Printf("Legacy: %v\n", computer.Legacy) + fmt.Printf("Facts:\n") + for i, fact := range computer.NotableFacts { + fmt.Printf("%d. %s\n", i+1, fact) + } +} diff --git a/examples/chat/vision/main.go b/examples/volc/chat/vision/main.go similarity index 90% rename from examples/chat/vision/main.go rename to examples/volc/chat/vision/main.go index adbea1b..be42558 100644 --- a/examples/chat/vision/main.go +++ b/examples/volc/chat/vision/main.go @@ -15,11 +15,11 @@ import ( /** * Authentication * If you authorize your endpoint using an API key, you can set your api key to environment variable "ARK_API_KEY" - * client := arkruntime.NewClientWithApiKey(os.Getenv("ARK_API_KEY")) + * client := arkruntime.NewVolcClientWithApiKey(os.Getenv("ARK_API_KEY")) */ func main() { - client := arkruntime.NewClientWithApiKey(os.Getenv("ARK_API_KEY")) + client := arkruntime.NewVolcClientWithApiKey(os.Getenv("ARK_API_KEY")) ctx := context.Background() textPart := chat.ChatCompletionContentPart{ @@ -42,7 +42,7 @@ func main() { } req := &chat.ChatCompletionRequest{ - Model: "${YOUR_ENDPOINT_ID}", + Model: "doubao-seed-2-1-pro-260628", Messages: []chat.ChatCompletionRequestMessage{ { OneOf: chat.NewChatCompletionRequestUserMessageChatCompletionRequestMessageSum( diff --git a/examples/contentgeneration/main.go b/examples/volc/contentgeneration/main.go similarity index 92% rename from examples/contentgeneration/main.go rename to examples/volc/contentgeneration/main.go index 687f818..bac1806 100644 --- a/examples/contentgeneration/main.go +++ b/examples/volc/contentgeneration/main.go @@ -17,13 +17,13 @@ import ( * Authentication * If you authorize your endpoint using an API key, set your api key to the * environment variable "ARK_API_KEY": - * client := arkruntime.NewClientWithApiKey(os.Getenv("ARK_API_KEY")) + * client := arkruntime.NewVolcClientWithApiKey(os.Getenv("ARK_API_KEY")) * Note: API keys do not refresh — pick one with no expiration. */ func main() { - client := arkruntime.NewClientWithApiKey(os.Getenv("ARK_API_KEY")) + client := arkruntime.NewVolcClientWithApiKey(os.Getenv("ARK_API_KEY")) ctx := context.Background() - modelEp := os.Getenv("ENDPOINT_ID") + modelEp := envOrDefault("SEEDANCE_MODEL", "doubao-seedance-2-0-fast-260128") // imageURL := os.Getenv("IMAGE_URL") fmt.Println("----- create content generation task -----") @@ -140,3 +140,10 @@ func main() { } } + +func envOrDefault(name, fallback string) string { + if value := os.Getenv(name); value != "" { + return value + } + return fallback +} diff --git a/examples/embeddings/main.go b/examples/volc/embeddings/main.go similarity index 84% rename from examples/embeddings/main.go rename to examples/volc/embeddings/main.go index 4939313..3f1c354 100644 --- a/examples/embeddings/main.go +++ b/examples/volc/embeddings/main.go @@ -16,18 +16,18 @@ import ( /** * Authentication * If you authorize your endpoint using an API key, you can set your api key to environment variable "ARK_API_KEY" - * client := arkruntime.NewClientWithApiKey(os.Getenv("ARK_API_KEY")) + * client := arkruntime.NewVolcClientWithApiKey(os.Getenv("ARK_API_KEY")) */ func main() { - client := arkruntime.NewClientWithApiKey( + client := arkruntime.NewVolcClientWithApiKey( os.Getenv("ARK_API_KEY"), ) ctx := context.Background() fmt.Println("----- embeddings request -----") req := &embedding.EmbeddingRequest{ - Model: "${YOUR_ENDPOINT_ID}", + Model: "doubao-embedding-large-text-250515", Input: embedding.NewStringArrayEmbeddingInput([]string{ "花椰菜又称菜花、花菜,是一种常见的蔬菜。", }), diff --git a/examples/environments/main.go b/examples/volc/environments/main.go similarity index 97% rename from examples/environments/main.go rename to examples/volc/environments/main.go index b7fe15b..5091fa6 100644 --- a/examples/environments/main.go +++ b/examples/volc/environments/main.go @@ -28,7 +28,7 @@ func main() { log.Fatal("set ARK_API_KEY") } - client := arkruntime.NewClientWithApiKey(apiKey) + client := arkruntime.NewVolcClientWithApiKey(apiKey) ctx := context.Background() // 1. Create — cloud + unrestricted network. diff --git a/examples/files/main.go b/examples/volc/files/main.go similarity index 96% rename from examples/files/main.go rename to examples/volc/files/main.go index 2e6d281..44796c3 100644 --- a/examples/files/main.go +++ b/examples/volc/files/main.go @@ -26,7 +26,7 @@ func main() { log.Fatal("set ARK_API_KEY") } - client := arkruntime.NewClientWithApiKey(apiKey) + client := arkruntime.NewVolcClientWithApiKey(apiKey) ctx := context.Background() f, err := os.Open(*target) diff --git a/examples/volc/images/main.go b/examples/volc/images/main.go new file mode 100644 index 0000000..7304a98 --- /dev/null +++ b/examples/volc/images/main.go @@ -0,0 +1,63 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "context" + "fmt" + "os" + + "github.com/volcengine/ark-runtime-go/arkruntime" + "github.com/volcengine/ark-runtime-go/arkruntime/model/images" +) + +/** + * Authentication + * If you authorize your endpoint using an API key, set your api key to the + * environment variable "ARK_API_KEY": + * client := arkruntime.NewVolcClientWithApiKey(os.Getenv("ARK_API_KEY")) + * Note: API keys do not refresh — pick one with no expiration. + */ +func main() { + client := arkruntime.NewVolcClientWithApiKey(os.Getenv("ARK_API_KEY")) + ctx := context.Background() + seedreamModel := envOrDefault("SEEDREAM_MODEL", "doubao-seedream-5-0-pro-260628") + + fmt.Println("----- [Seedream] generate images (response format: url) -----") + req := &images.CreateImageGenerationRequest{ + Model: seedreamModel, + Prompt: "龙与地下城女骑士背景是起伏的平原,目光从镜头转向平原", + ResponseFormat: images.NewOptResponseFormat(images.ResponseFormatURL), + Seed: images.NewOptInt64(1234567890), + Watermark: images.NewOptBool(true), + Size: images.NewOptString("1024x1024"), + } + + resp, err := client.GenerateImages(ctx, req) + if err != nil { + fmt.Printf("generate images error: %v\n", err) + return + } + if resp.Error.IsSet() { + fmt.Printf("Error Code: %s\n", resp.Error.Value.Code) + fmt.Printf("Error Message: %s\n", resp.Error.Value.Message) + return + } + fmt.Printf("Model: %s\n", resp.Model) + if len(resp.Data) > 0 { + fmt.Printf("Image URL: %s\n", resp.Data[0].URL.Value) + } + if resp.Usage.IsSet() { + fmt.Printf("Generated Images: %d\n", resp.Usage.Value.GeneratedImages) + } + fmt.Printf("Created: %d\n", resp.Created) + +} + +func envOrDefault(name, fallback string) string { + if value := os.Getenv(name); value != "" { + return value + } + return fallback +} diff --git a/examples/listinputitems/main.go b/examples/volc/listinputitems/main.go similarity index 95% rename from examples/listinputitems/main.go rename to examples/volc/listinputitems/main.go index ef3475b..025796c 100644 --- a/examples/listinputitems/main.go +++ b/examples/volc/listinputitems/main.go @@ -21,7 +21,7 @@ import ( const defaultLimit int32 = 10 func main() { - client := arkruntime.NewClientWithApiKey(os.Getenv("ARK_API_KEY")) + client := arkruntime.NewVolcClientWithApiKey(os.Getenv("ARK_API_KEY")) ctx := context.Background() resp, err := client.CreateResponses(ctx, &responses.ResponsesRequest{ diff --git a/examples/memory_stores/main.go b/examples/volc/memory_stores/main.go similarity index 98% rename from examples/memory_stores/main.go rename to examples/volc/memory_stores/main.go index b4c88dd..7f468f2 100644 --- a/examples/memory_stores/main.go +++ b/examples/volc/memory_stores/main.go @@ -38,7 +38,7 @@ func main() { log.Fatal("set ARK_API_KEY") } - client := arkruntime.NewClientWithApiKey(apiKey) + client := arkruntime.NewVolcClientWithApiKey(apiKey) ctx := context.Background() // 1. Create a memory store. diff --git a/examples/multimodalembeddings/main.go b/examples/volc/multimodalembeddings/main.go similarity index 87% rename from examples/multimodalembeddings/main.go rename to examples/volc/multimodalembeddings/main.go index ce2353a..c7c82fd 100644 --- a/examples/multimodalembeddings/main.go +++ b/examples/volc/multimodalembeddings/main.go @@ -16,18 +16,18 @@ import ( /** * Authentication * If you authorize your endpoint using an API key, you can set your api key to environment variable "ARK_API_KEY" - * client := arkruntime.NewClientWithApiKey(os.Getenv("ARK_API_KEY")) + * client := arkruntime.NewVolcClientWithApiKey(os.Getenv("ARK_API_KEY")) */ func main() { - client := arkruntime.NewClientWithApiKey( + client := arkruntime.NewVolcClientWithApiKey( os.Getenv("ARK_API_KEY"), ) ctx := context.Background() fmt.Println("----- multimodal embeddings request -----") req := &multimodalembedding.MultiModalEmbeddingRequest{ - Model: "doubao-embedding-vision-250615", + Model: "doubao-embedding-vision-251215", Input: []multimodalembedding.EmbeddingInput{ { Type: multimodalembedding.EmbeddingInputTypeImageURL, diff --git a/examples/responses/basic/main.go b/examples/volc/responses/basic/main.go similarity index 95% rename from examples/responses/basic/main.go rename to examples/volc/responses/basic/main.go index 16e7335..26a5652 100644 --- a/examples/responses/basic/main.go +++ b/examples/volc/responses/basic/main.go @@ -16,11 +16,11 @@ import ( /** * Authentication * If you authorize your endpoint using an API key, you can set your api key to environment variable "ARK_API_KEY" - * client := arkruntime.NewClientWithApiKey(os.Getenv("ARK_API_KEY")) + * client := arkruntime.NewVolcClientWithApiKey(os.Getenv("ARK_API_KEY")) */ func main() { - client := arkruntime.NewClientWithApiKey(os.Getenv("ARK_API_KEY")) + client := arkruntime.NewVolcClientWithApiKey(os.Getenv("ARK_API_KEY")) ctx := context.Background() fmt.Println("----- round 1 message -----") diff --git a/examples/responses/doubao_app/main.go b/examples/volc/responses/doubao_app/main.go similarity index 95% rename from examples/responses/doubao_app/main.go rename to examples/volc/responses/doubao_app/main.go index 247db4f..3bb7193 100644 --- a/examples/responses/doubao_app/main.go +++ b/examples/volc/responses/doubao_app/main.go @@ -5,7 +5,7 @@ // (chat / deep_chat / ai_search / reasoning_search) through the // Responses API, in both streaming and non-streaming variants. // -// The DoubaoApp tool is currently behind the ``ark-beta-doubao-app: true`` +// The DoubaoApp tool is currently behind the “ark-beta-doubao-app: true“ // request header, which the example sets on every call. // // Run with: @@ -43,14 +43,14 @@ var featureToQuery = map[string]string{ } func main() { - stream(chatFeature) // change to deepChatFeature, aiSearchFeature, reasoningSearchFeature to test other features + stream(chatFeature) // change to deepChatFeature, aiSearchFeature, reasoningSearchFeature to test other features fmt.Println() nonStream(chatFeature) } func nonStream(feature string) { fmt.Println("===== non-streaming =====") - client := arkruntime.NewClientWithApiKey(os.Getenv("ARK_API_KEY")) + client := arkruntime.NewVolcClientWithApiKey(os.Getenv("ARK_API_KEY")) ctx := context.Background() fmt.Println("----- round 1 message -----") @@ -86,7 +86,7 @@ func nonStream(feature string) { func stream(feature string) { fmt.Println("===== streaming =====") - client := arkruntime.NewClientWithApiKey(os.Getenv("ARK_API_KEY")) + client := arkruntime.NewVolcClientWithApiKey(os.Getenv("ARK_API_KEY")) ctx := context.Background() fmt.Println("----- round 1 message -----") diff --git a/examples/responses/function_call/main.go b/examples/volc/responses/function_call/main.go similarity index 98% rename from examples/responses/function_call/main.go rename to examples/volc/responses/function_call/main.go index e9bd689..d1f4a7e 100644 --- a/examples/responses/function_call/main.go +++ b/examples/volc/responses/function_call/main.go @@ -27,7 +27,7 @@ import ( const modelName = "doubao-seed-2-1-pro-260628" func main() { - client := arkruntime.NewClientWithApiKey(os.Getenv("ARK_API_KEY")) + client := arkruntime.NewVolcClientWithApiKey(os.Getenv("ARK_API_KEY")) ctx := context.Background() sumTool := responses.Tool{ diff --git a/examples/responses/mcp/main.go b/examples/volc/responses/mcp/main.go similarity index 93% rename from examples/responses/mcp/main.go rename to examples/volc/responses/mcp/main.go index 81dd34f..5439dff 100644 --- a/examples/responses/mcp/main.go +++ b/examples/volc/responses/mcp/main.go @@ -4,9 +4,9 @@ // MCP tool example: registers a remote MCP server (deepwiki) as a tool, // walks through the human-in-the-loop approval flow when the model asks // to call it, and demonstrates skipping approval entirely with -// ``RequireApproval = never``. Both streaming and non-streaming variants. +// “RequireApproval = never“. Both streaming and non-streaming variants. // -// MCP support is currently behind the ``ark-beta-mcp: true`` request +// MCP support is currently behind the “ark-beta-mcp: true“ request // header, which the example sets on every call. // // Run with: @@ -40,14 +40,14 @@ func main() { // nonStream demonstrates the three rounds with non-streaming requests. // -// 1. Send the prompt → model returns an mcp_approval_request. -// 2. Send back an mcp_approval_response with approve=true → model now -// executes the call and returns the answer. -// 3. Re-send the original prompt with RequireApproval=never to skip the -// approval roundtrip entirely. +// 1. Send the prompt → model returns an mcp_approval_request. +// 2. Send back an mcp_approval_response with approve=true → model now +// executes the call and returns the answer. +// 3. Re-send the original prompt with RequireApproval=never to skip the +// approval roundtrip entirely. func nonStream() { fmt.Println("===== non-streaming =====") - client := arkruntime.NewClientWithApiKey(os.Getenv("ARK_API_KEY")) + client := arkruntime.NewVolcClientWithApiKey(os.Getenv("ARK_API_KEY")) ctx := context.Background() // Round 1: ask, model responds with an approval request. @@ -116,7 +116,7 @@ func nonStream() { // final response object. func stream() { fmt.Println("===== streaming =====") - client := arkruntime.NewClientWithApiKey(os.Getenv("ARK_API_KEY")) + client := arkruntime.NewVolcClientWithApiKey(os.Getenv("ARK_API_KEY")) ctx := context.Background() fmt.Println("----- round 1: prompt → approval request (streaming) -----") diff --git a/examples/volc/responses/video/main.go b/examples/volc/responses/video/main.go new file mode 100644 index 0000000..4952ea3 --- /dev/null +++ b/examples/volc/responses/video/main.go @@ -0,0 +1,139 @@ +// Copyright (c) 2026 ByteDance Ltd. and/or its affiliates. +// SPDX-License-Identifier: Apache-2.0 + +// Video input example: uploads a local video file via the Files API, +// waits for processing to finish, then asks the model to describe each +// second of the video. Round 2 chains a follow-up question via +// PreviousResponseID so the model can reason over the same video without +// re-uploading. +// +// Run with: +// +// ARK_API_KEY=... go run ./examples/responses/video +// +// Expects ./ark_vlm_video_input.mp4 in the working directory. +package main + +import ( + "context" + "fmt" + "io" + "os" + + "github.com/volcengine/ark-runtime-go/arkruntime" + "github.com/volcengine/ark-runtime-go/arkruntime/model/file" + "github.com/volcengine/ark-runtime-go/arkruntime/model/responses" +) + +const modelName = "doubao-seed-2-1-pro-260628" + +func main() { + client := arkruntime.NewVolcClientWithApiKey(os.Getenv("ARK_API_KEY")) + ctx := context.Background() + + fileID, err := uploadAndAwait(ctx, client, "ark_vlm_video_input.mp4") + if err != nil { + fmt.Printf("upload error: %v\n", err) + return + } + + // Round 1: video + text prompt asking for a per-second description. + round1Msg := responses.ItemEasyMessage{ + Role: responses.NewOptMessageRole(responses.MessageRoleUser), + Content: responses.NewContentItemArrayMessageContent([]responses.ContentItem{ + { + OneOf: responses.NewContentItemVideoContentItemSum(responses.ContentItemVideo{ + Type: responses.ContentItemVideoTypeInputVideo, + FileID: responses.NewOptString(fileID), + }), + }, + { + OneOf: responses.NewContentItemTextContentItemSum(responses.ContentItemText{ + Type: responses.ContentItemTextTypeInputText, + Text: "请逐帧给出视频中每一秒的描述", + }), + }, + }), + } + req := &responses.ResponsesRequest{ + Model: modelName, + Input: responses.NewInputItemArrayResponsesInput([]responses.InputItem{ + {OneOf: responses.NewItemEasyMessageInputItemSum(round1Msg)}, + }), + } + + fmt.Println("----- round 1: per-second description -----") + responseID, err := streamAndCaptureID(ctx, client, req) + if err != nil { + fmt.Printf("round 1 stream error: %v\n", err) + return + } + + // Round 2: chain via PreviousResponseID. No need to re-send the video. + fmt.Println("\n----- round 2: ask a follow-up about the same video -----") + req.Input = responses.NewStringResponsesInput("上述对话中有什么?") + req.PreviousResponseID = responses.NewOptString(responseID) + if _, err := streamAndCaptureID(ctx, client, req); err != nil { + fmt.Printf("round 2 stream error: %v\n", err) + return + } + fmt.Println() +} + +// uploadAndAwait uploads a local file and polls until processing finishes. +func uploadAndAwait(ctx context.Context, client *arkruntime.Client, path string) (string, error) { + data, err := os.Open(path) + if err != nil { + return "", fmt.Errorf("open %s: %w", path, err) + } + defer data.Close() + + fmt.Println("----- upload video -----") + meta, err := client.UploadFile(ctx, &file.FileCreateRequest{ + Purpose: file.PurposeUserData, + }, data) + if err != nil { + return "", fmt.Errorf("upload: %w", err) + } + + ready, err := client.WaitForFileProcessing(ctx, meta.ID, arkruntime.WaitForFileProcessingOptions{}) + if err != nil { + return "", fmt.Errorf("wait %s: %w", meta.ID, err) + } + fmt.Printf("video ready: id=%s status=%s\n", ready.ID, ready.Status) + return ready.ID, nil +} + +// streamAndCaptureID prints reasoning + text deltas and returns the +// response_id from the first ResponseCreated event so the caller can chain +// a follow-up turn via PreviousResponseID. +func streamAndCaptureID( + ctx context.Context, + client *arkruntime.Client, + req *responses.ResponsesRequest, +) (string, error) { + stream, err := client.CreateResponsesStream(ctx, req) + if err != nil { + return "", err + } + var responseID string + for { + event, err := stream.Recv() + if err == io.EOF { + return responseID, nil + } + if err != nil { + return responseID, err + } + switch event.OneOf.Type { + case responses.ResponseCreatedEventResponseStreamEventSum: + responseID = event.OneOf.ResponseCreatedEvent.Response.ID + case responses.ResponseReasoningSummaryTextDeltaEventResponseStreamEventSum: + fmt.Print(event.OneOf.ResponseReasoningSummaryTextDeltaEvent.Delta.Or("")) + case responses.ResponseTextDeltaEventResponseStreamEventSum: + fmt.Print(event.OneOf.ResponseTextDeltaEvent.Delta.Or("")) + case responses.ResponseTextDoneEventResponseStreamEventSum: + fmt.Printf("\n[text done] %s\n", event.OneOf.ResponseTextDoneEvent.Text.Or("")) + } + } +} diff --git a/examples/responses/web_search/main.go b/examples/volc/responses/web_search/main.go similarity index 87% rename from examples/responses/web_search/main.go rename to examples/volc/responses/web_search/main.go index 9278f6f..dfc82d3 100644 --- a/examples/responses/web_search/main.go +++ b/examples/volc/responses/web_search/main.go @@ -21,6 +21,7 @@ import ( ) const modelName = "doubao-seed-2-1-pro-260628" +const webSearchBetaHeader = "ark-beta-web-search" func main() { stream() @@ -30,10 +31,10 @@ func main() { func nonStream() { fmt.Println("===== non-streaming =====") - client := arkruntime.NewClientWithApiKey(os.Getenv("ARK_API_KEY")) + client := arkruntime.NewVolcClientWithApiKey(os.Getenv("ARK_API_KEY")) ctx := context.Background() - resp, err := client.CreateResponses(ctx, buildRequest()) + resp, err := client.CreateResponses(ctx, buildRequest(), arkruntime.WithCustomHeader(webSearchBetaHeader, "true")) if err != nil { fmt.Printf("create error: %v\n", err) return @@ -43,10 +44,10 @@ func nonStream() { func stream() { fmt.Println("===== streaming =====") - client := arkruntime.NewClientWithApiKey(os.Getenv("ARK_API_KEY")) + client := arkruntime.NewVolcClientWithApiKey(os.Getenv("ARK_API_KEY")) ctx := context.Background() - s, err := client.CreateResponsesStream(ctx, buildRequest()) + s, err := client.CreateResponsesStream(ctx, buildRequest(), arkruntime.WithCustomHeader(webSearchBetaHeader, "true")) if err != nil { fmt.Printf("stream error: %v\n", err) return diff --git a/examples/sessions_loop/main.go b/examples/volc/sessions_loop/main.go similarity index 98% rename from examples/sessions_loop/main.go rename to examples/volc/sessions_loop/main.go index dc1613c..46d6573 100644 --- a/examples/sessions_loop/main.go +++ b/examples/volc/sessions_loop/main.go @@ -42,7 +42,7 @@ func main() { modelID = "${YOUR_MODEL_ID}" } - client := arkruntime.NewClientWithApiKey(apiKey) + client := arkruntime.NewVolcClientWithApiKey(apiKey) ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second) defer cancel() diff --git a/examples/sparseembeddings/main.go b/examples/volc/sparseembeddings/main.go similarity index 88% rename from examples/sparseembeddings/main.go rename to examples/volc/sparseembeddings/main.go index 60376d0..05098d3 100644 --- a/examples/sparseembeddings/main.go +++ b/examples/volc/sparseembeddings/main.go @@ -16,18 +16,18 @@ import ( /** * Authentication * If you authorize your endpoint using an API key, you can set your api key to environment variable "ARK_API_KEY" - * client := arkruntime.NewClientWithApiKey(os.Getenv("ARK_API_KEY")) + * client := arkruntime.NewVolcClientWithApiKey(os.Getenv("ARK_API_KEY")) */ func main() { - client := arkruntime.NewClientWithApiKey( + client := arkruntime.NewVolcClientWithApiKey( os.Getenv("ARK_API_KEY"), ) ctx := context.Background() fmt.Println("----- sparse embeddings request -----") req := &multimodalembedding.MultiModalEmbeddingRequest{ - Model: "doubao-embedding-vision-250615", + Model: "doubao-embedding-vision-251215", Input: []multimodalembedding.EmbeddingInput{ { Type: multimodalembedding.EmbeddingInputTypeText, diff --git a/examples/tokenization/main.go b/examples/volc/tokenization/main.go similarity index 88% rename from examples/tokenization/main.go rename to examples/volc/tokenization/main.go index cd4fa93..ec241b2 100644 --- a/examples/tokenization/main.go +++ b/examples/volc/tokenization/main.go @@ -16,11 +16,11 @@ import ( /** * Authentication * If you authorize your endpoint using an API key, you can set your api key to environment variable "ARK_API_KEY" - * client := arkruntime.NewClientWithApiKey(os.Getenv("ARK_API_KEY")) + * client := arkruntime.NewVolcClientWithApiKey(os.Getenv("ARK_API_KEY")) */ func main() { - client := arkruntime.NewClientWithApiKey( + client := arkruntime.NewVolcClientWithApiKey( os.Getenv("ARK_API_KEY"), ) ctx := context.Background()