Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@ go.work.sum
/.vscode
/output
coverage.out
/apb
/apb
.coda/
17 changes: 16 additions & 1 deletion client.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,8 @@ func GetPrompt(ctx context.Context, param GetPromptParam, options ...GetPromptOp

// PromptFormat format prompt with variables
func PromptFormat(ctx context.Context, prompt *entity.Prompt, variables map[string]any, options ...PromptFormatOption) (
messages []*entity.Message, err error) {
messages []*entity.Message, err error,
) {
return getDefaultClient().PromptFormat(ctx, prompt, variables, options...)
}

Expand Down Expand Up @@ -495,6 +496,20 @@ func (c *loopClient) PromptFormat(ctx context.Context, loopPrompt *entity.Prompt
return c.promptProvider.PromptFormat(ctx, loopPrompt, variables, config)
}

func (c *loopClient) Execute(ctx context.Context, req *entity.ExecuteParam, options ...ExecuteOption) (entity.ExecuteResult, error) {
if c.closed {
return entity.ExecuteResult{}, consts.ErrClientClosed
}
return c.promptProvider.Execute(ctx, req, options...)
}

func (c *loopClient) ExecuteStreaming(ctx context.Context, req *entity.ExecuteParam, options ...ExecuteStreamingOption) (entity.StreamReader[entity.ExecuteResult], error) {
if c.closed {
return nil, consts.ErrClientClosed
}
return c.promptProvider.ExecuteStreaming(ctx, req, options...)
}

func (c *loopClient) StartSpan(ctx context.Context, name, spanType string, opts ...StartSpanOption) (context.Context, Span) {
if c.closed {
return ctx, DefaultNoopSpan
Expand Down
58 changes: 49 additions & 9 deletions entity/prompt.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@

package entity

import "github.com/coze-dev/cozeloop-go/internal/util"
import (
"github.com/coze-dev/cozeloop-go/internal/util"
)

type Prompt struct {
WorkspaceID string `json:"workspace_id"`
Expand All @@ -29,9 +31,24 @@ const (
)

type Message struct {
Role Role `json:"role"`
Content *string `json:"content,omitempty"`
Parts []*ContentPart `json:"parts,omitempty"`
Role Role `json:"role"`
ReasoningContent *string `json:"reasoning_content,omitempty"`
Content *string `json:"content,omitempty"`
Parts []*ContentPart `json:"parts,omitempty"`
ToolCallID *string `json:"tool_call_id,omitempty"`
ToolCalls []*ToolCall `json:"tool_calls,omitempty"`
}

type ToolCall struct {
Index int32 `json:"index"`
ID string `json:"id"`
Type ToolType `json:"type"`
FunctionCall *FunctionCall `json:"function_call,omitempty"`
}

type FunctionCall struct {
Name string `json:"name"`
Arguments *string `json:"arguments,omitempty"`
}

type Role string
Expand All @@ -45,16 +62,18 @@ const (
)

type ContentPart struct {
Type ContentType `json:"type"`
Text *string `json:"text,omitempty"`
ImageURL *string `json:"image_url,omitempty"`
Type ContentType `json:"type"`
Text *string `json:"text,omitempty"`
ImageURL *string `json:"image_url,omitempty"`
Base64Data *string `json:"base64_data,omitempty"`
}

type ContentType string

const (
ContentTypeText ContentType = "text"
ContentTypeImageURL ContentType = "image_url"
ContentTypeBase64Data ContentType = "base64_data"
ContentTypeMultiPartVariable ContentType = "multi_part_variable"
)

Expand Down Expand Up @@ -119,6 +138,25 @@ type LLMConfig struct {
JSONMode *bool `json:"json_mode,omitempty"`
}

type ExecuteParam struct {
PromptKey string `json:"prompt_key"`
Version string `json:"version,omitempty"`
Label string `json:"label,omitempty"`
VariableVals map[string]any `json:"variable_vals,omitempty"`
Messages []*Message `json:"messages,omitempty"`
}

type ExecuteResult struct {
Message *Message `json:"message,omitempty"`
FinishReason *string `json:"finish_reason,omitempty"`
Usage *TokenUsage `json:"usage,omitempty"`
}

type TokenUsage struct {
InputTokens int `json:"input_tokens"`
OutputTokens int `json:"output_tokens"`
}

func (p *Prompt) DeepCopy() *Prompt {
if p == nil {
return nil
Expand Down Expand Up @@ -181,12 +219,14 @@ func (cp *ContentPart) DeepCopy() *ContentPart {
return nil
}
copied := &ContentPart{
Type: cp.Type,
ImageURL: cp.ImageURL,
Type: cp.Type,
}
if cp.Text != nil {
copied.Text = util.Ptr(*cp.Text)
}
if cp.ImageURL != nil {
copied.ImageURL = util.Ptr(*cp.ImageURL)
}
return copied
}

Expand Down
8 changes: 8 additions & 0 deletions entity/stream.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
// SPDX-License-Identifier: MIT

package entity

type StreamReader[T any] interface {
Recv() (T, error)
}
6 changes: 4 additions & 2 deletions error.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,7 @@ var (
ErrParsePrivateKey = consts.ErrParsePrivateKey
)

type AuthError = consts.AuthError
type RemoteServiceError = consts.RemoteServiceError
type (
AuthError = consts.AuthError
RemoteServiceError = consts.RemoteServiceError
)
3 changes: 1 addition & 2 deletions examples/init/log/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ func main() {
cozeloop.Close(ctx)
}

type CustomLogger struct {
}
type CustomLogger struct{}

func (l *CustomLogger) CtxDebugf(ctx context.Context, format string, v ...interface{}) {
fmt.Printf("[Custom] [DEBUG] "+format+"\n", v...)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,8 @@ func (r *llmRunner) llmCall(ctx context.Context, messages []*entity.Message) (er
defer span.Finish(ctx)

// llm is processing
//baseURL := "https://xxx"
//ak := "****"
// baseURL := "https://xxx"
// ak := "****"
modelName := "gpt-4o-2024-05-13"
maxTokens := 1000 // range: [0, 4096]
//transport := &MyTransport{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,8 @@ func (r *llmRunner) llmCall(ctx context.Context, messages []*entity.Message) (er
defer span.Finish(ctx)

// llm is processing
//baseURL := "https://xxx"
//ak := "****"
// baseURL := "https://xxx"
// ak := "****"
modelName := "gpt-4o-2024-05-13"
maxTokens := 1000 // range: [0, 4096]
//transport := &MyTransport{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,8 @@ func (r *llmRunner) llmCall(ctx context.Context, messages []*entity.Message) (er
defer span.Finish(ctx)

// llm is processing
//baseURL := "https://xxx"
//ak := "****"
// baseURL := "https://xxx"
// ak := "****"
modelName := "gpt-4o-2024-05-13"
maxTokens := 1000 // range: [0, 4096]
//transport := &MyTransport{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func main() {

// 4.Format messages of the prompt
imageText := "图片样例"
imageURL := "https://example.com" //公网访问地址
imageURL := "https://example.com" // 公网访问地址
messages, err := llmRunner.client.PromptFormat(ctx, prompt, map[string]any{
"num": "2",
"count": 10,
Expand Down Expand Up @@ -131,8 +131,8 @@ func (r *llmRunner) llmCall(ctx context.Context, messages []*entity.Message) (er
defer span.Finish(ctx)

// llm is processing
//baseURL := "https://xxx"
//ak := "****"
// baseURL := "https://xxx"
// ak := "****"
modelName := "gpt-4o-2024-05-13"
maxTokens := 1000 // range: [0, 4096]
//transport := &MyTransport{
Expand Down
92 changes: 92 additions & 0 deletions examples/prompt/ptaas/ptaas.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
// SPDX-License-Identifier: MIT

package main

import (
"context"
"fmt"
"io"

"github.com/coze-dev/cozeloop-go"
"github.com/coze-dev/cozeloop-go/entity"
"github.com/coze-dev/cozeloop-go/internal/util"
)

func main() {
// 1.Create a prompt on the platform
// Create a Prompt on the platform's Prompt development page (set Prompt Key to 'ptaas_demo'),
// add the following messages to the template, submit a version.
// System: You are a helpful assistant for {{topic}}.
// User: Please help me with {{user_request}}
ctx := context.Background()

// Set the following environment variables first.
// COZELOOP_WORKSPACE_ID=your workspace id
// COZELOOP_API_TOKEN=your token
// 2.New loop client
client, err := cozeloop.NewClient()
if err != nil {
panic(err)
}
defer client.Close(ctx)

// 3. Execute prompt
executeRequest := &entity.ExecuteParam{
PromptKey: "ptaas_demo",
Version: "0.0.1",
VariableVals: map[string]any{
"topic": "artificial intelligence",
"user_request": "explain what is machine learning",
},
// You can also append messages to the prompt.
Messages: []*entity.Message{
{
Role: entity.RoleUser,
Content: util.Ptr("Keep the answer brief."),
},
},
}
// 3.1 non stream
nonStream(ctx, client, executeRequest)
// 3.2 stream
stream(ctx, client, executeRequest)
}

func nonStream(ctx context.Context, client cozeloop.Client, executeRequest *entity.ExecuteParam) {
result, err := client.Execute(ctx, executeRequest)
if err != nil {
panic(err)
}
printExecuteResult(result)
}

func stream(ctx context.Context, client cozeloop.Client, executeRequest *entity.ExecuteParam) {
streamReader, err := client.ExecuteStreaming(ctx, executeRequest)
if err != nil {
panic(err)
}
for {
result, err := streamReader.Recv()
if err != nil {
if err == io.EOF {
fmt.Println("\nStream finished.")
break
}
panic(err)
}
printExecuteResult(result)
}
}

func printExecuteResult(result entity.ExecuteResult) {
if result.Message != nil {
fmt.Printf("Message: %s\n", util.ToJSON(result.Message))
}
if util.PtrValue(result.FinishReason) != "" {
fmt.Printf("FinishReason: %s\n", util.PtrValue(result.FinishReason))
}
if result.Usage != nil {
fmt.Printf("Usage: %s\n", util.ToJSON(result.Usage))
}
}
71 changes: 71 additions & 0 deletions examples/prompt/ptaas/ptaas_jinja/ptaas_jinja.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
// SPDX-License-Identifier: MIT

package main

import (
"context"
"fmt"

"github.com/coze-dev/cozeloop-go"
"github.com/coze-dev/cozeloop-go/entity"
"github.com/coze-dev/cozeloop-go/internal/util"
)

// The explanation of jinja2 template is based on non-streaming execution, and it also applies to streaming execution.
func main() {
// 1.Create a prompt using jinja2 template on the platform
// Create a Prompt on the platform's Prompt development page (set Prompt Key to 'ptaas_demo'),
// add the following messages to the template, submit a version, and set a label (e.g., 'production') for that version.
// System: You are a helpful assistant for {{param.topic}}. Your audience is {{param.age}} years old.
// User: Please help me with {{param.user_request}}
ctx := context.Background()

// Set the following environment variables first.
// COZELOOP_WORKSPACE_ID=your workspace id
// COZELOOP_API_TOKEN=your token
// 2.New loop client
client, err := cozeloop.NewClient()
if err != nil {
panic(err)
}
defer client.Close(ctx)

// 3. Execute prompt
executeRequest := &entity.ExecuteParam{
PromptKey: "ptaas_demo",
Version: "0.0.2",
VariableVals: map[string]any{
"param": struct {
Topic string `json:"topic"`
Age int `json:"age"`
UserRequest string `json:"user_request"`
}{
Topic: "artificial intelligence",
Age: 10,
UserRequest: "explain what is machine learning",
},
},
}
nonStream(ctx, client, executeRequest)
}

func nonStream(ctx context.Context, client cozeloop.Client, executeRequest *entity.ExecuteParam) {
result, err := client.Execute(ctx, executeRequest)
if err != nil {
panic(err)
}
printExecuteResult(result)
}

func printExecuteResult(result entity.ExecuteResult) {
if result.Message != nil {
fmt.Printf("Message: %s\n", util.ToJSON(result.Message))
}
if util.PtrValue(result.FinishReason) != "" {
fmt.Printf("FinishReason: %s\n", util.PtrValue(result.FinishReason))
}
if result.Usage != nil {
fmt.Printf("Usage: %s\n", util.ToJSON(result.Usage))
}
}
Loading
Loading