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
14 changes: 12 additions & 2 deletions entity/prompt.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type TemplateType string

const (
TemplateTypeNormal TemplateType = "normal"
TemplateTypeJinja2 TemplateType = "jinja2"
)

type Message struct {
Expand Down Expand Up @@ -57,8 +58,17 @@ type VariableDef struct {
type VariableType string

const (
VariableTypeString VariableType = "string"
VariableTypePlaceholder VariableType = "placeholder"
VariableTypeString VariableType = "string"
VariableTypePlaceholder VariableType = "placeholder"
VariableTypeBoolean VariableType = "boolean"
VariableTypeInteger VariableType = "integer"
VariableTypeFloat VariableType = "float"
VariableTypeObject VariableType = "object"
VariableTypeArrayString VariableType = "array<string>"
VariableTypeArrayBoolean VariableType = "array<boolean>"
VariableTypeArrayInteger VariableType = "array<integer>"
VariableTypeArrayFloat VariableType = "array<float>"
VariableTypeArrayObject VariableType = "array<object>"
)

type ToolChoiceType string
Expand Down
265 changes: 265 additions & 0 deletions examples/prompt/advance/prompt_hub_jinja.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
// SPDX-License-Identifier: MIT

package main

import (
"context"
"encoding/json"
"fmt"
"net/http"
"time"

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

// If you want to use the jinja templates in prompts, you can refer to the following.
func main() {
// 1.Create a prompt on the platform
// You can create a Prompt on the platform's Prompt development page (set Prompt Key to 'prompt_hub_demo'), add the following messages to the template, and submit a version.
// System: You are a helpful bot, the conversation topic is {{var1}}.
// Placeholder: placeholder1
// User: My question is {{var2}}
// Placeholder: placeholder2

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(
// Set whether to report a trace span when get or format prompt.
// Default value is false.
cozeloop.WithPromptTrace(true))
if err != nil {
panic(err)
}

llmRunner := llmRunner{
client: client,
}

// 1. start root span
ctx, span := llmRunner.client.StartSpan(ctx, "root_span", "main_span", nil)

// 3.Get the prompt
prompt, err := llmRunner.client.GetPrompt(ctx, cozeloop.GetPromptParam{
PromptKey: "prompt_hub_demo",
// If version is not specified, the latest version of the corresponding prompt will be obtained
Version: "0.0.1",
})
if err != nil {
fmt.Printf("get prompt failed: %v\n", err)
return
}
if prompt != nil {
// Get messages of the prompt
if prompt.PromptTemplate != nil {
messages, err := json.Marshal(prompt.PromptTemplate.Messages)
if err != nil {
fmt.Printf("json marshal failed: %v\n", err)
return
}
fmt.Printf("prompt messages=%s\n", string(messages))
}
// Get llm config of the prompt
if prompt.LLMConfig != nil {
llmConfig, err := json.Marshal(prompt.LLMConfig)
if err != nil {
fmt.Printf("json marshal failed: %v\n", err)
}
fmt.Printf("prompt llm config=%s\n", llmConfig)
}

// 4.Format messages of the prompt
userMessageContent := "Hello!"
assistantMessageContent := "Hello!"

messages, err := llmRunner.client.PromptFormat(ctx, prompt, map[string]any{
"var_string": "hi",
"var_int": 5,
"var_bool": true,
"var_float": 1.0,
"var_object": map[string]interface{}{
"name": "John",
"age": 30,
"hobbies": []string{"reading", "coding"},
"address": map[string]interface{}{
"city": "bejing",
"street": "123 Main",
},
},
"var_array_string": []string{
"hello", "nihao",
},
"var_array_boolean": []bool{
true, false, true,
},
"var_array_int": []int64{
1, 2, 3, 4,
},
"var_array_float": []float64{
1.0, 2.0,
},
"var_array_object": []interface{}{
map[string]interface{}{"key": "123"},
map[string]interface{}{"value": 100},
},
// Placeholder variable type should be entity.Message/*entity.Message/[]entity.Message/[]*entity.Message
"placeholder1": []*entity.Message{
{
Role: entity.RoleUser,
Content: &userMessageContent,
},
{
Role: entity.RoleAssistant,
Content: &assistantMessageContent,
},
},
// Other variables in the prompt template that are not provided with corresponding values will be considered as empty values
})
if err != nil {
fmt.Printf("prompt format failed: %v\n", err)
return
}
data, err := json.Marshal(messages)
if err != nil {
fmt.Printf("json marshal failed: %v\n", err)
return
}
fmt.Printf("formatted messages=%s\n", string(data))

// 5. llm call
err = llmRunner.llmCall(ctx, messages)
if err != nil {
return
}
}

// 6. span finish
span.Finish(ctx)

// 7. (optional) flush or close
// -- force flush, report all traces in the queue
// Warning! In general, this method is not needed to be call, as spans will be automatically reported in batches.
// Note that flush will block and wait for the report to complete, and it may cause frequent reporting,
// affecting performance.
llmRunner.client.Flush(ctx)
}

type llmRunner struct {
client cozeloop.Client
}

func (r *llmRunner) llmCall(ctx context.Context, messages []*entity.Message) (err error) {
ctx, span := r.client.StartSpan(ctx, "llmCall", tracespec.VModelSpanType, nil)
defer span.Finish(ctx)

// llm is processing
//baseURL := "https://xxx"
//ak := "****"
modelName := "gpt-4o-2024-05-13"
maxTokens := 1000 // range: [0, 4096]
//transport := &MyTransport{
// DefaultTransport: &http.Transport{},
//}
//config := openai.DefaultAzureConfig(ak, baseURL)
//config.HTTPClient = &http.Client{
// Transport: transport,
//}
//client := openai.NewClientWithConfig(config)
//marshal, err := json.Marshal(messages)
//if err != nil {
// return err
//}
//chatCompletionMessage := make([]openai.ChatCompletionMessage, 0)
//err = json.Unmarshal(marshal, &chatCompletionMessage)
//if err != nil {
// return err
//}
//resp, err := client.CreateChatCompletion(
// ctx,
// openai.ChatCompletionRequest{
// Model: modelName,
// Messages: chatCompletionMessage,
// MaxTokens: maxTokens,
// TopP: 0.95,
// N: 1,
// PresencePenalty: 1.0,
// FrequencyPenalty: 1.0,
// Temperature: 0.6,
// },
//)

// mock resp
time.Sleep(1 * time.Second)
respChoices := []string{
"Hello! Can I help you?",
}
respPromptTokens := 11
respCompletionTokens := 52

// set tag key: `input`
span.SetInput(ctx, convertModelInput(messages))
// set tag key: `output`
span.SetOutput(ctx, respChoices)
// set tag key: `model_provider`, e.g., openai, etc.
span.SetModelProvider(ctx, "openai")
// set tag key: `start_time_first_resp`
// Timestamp of the first packet return from LLM, unit: microseconds.
// When `start_time_first_resp` is set, a tag named `latency_first_resp` calculated
// based on the span's StartTime will be added, meaning the latency for the first packet.
span.SetStartTimeFirstResp(ctx, time.Now().UnixMicro())
// set tag key: `input_tokens`. The amount of input tokens.
// when the `input_tokens` value is set, it will automatically sum with the `output_tokens` to calculate the `tokens` tag.
span.SetInputTokens(ctx, respPromptTokens)
// set tag key: `output_tokens`. The amount of output tokens.
// when the `output_tokens` value is set, it will automatically sum with the `input_tokens` to calculate the `tokens` tag.
span.SetOutputTokens(ctx, respCompletionTokens)
// set tag key: `model_name`, e.g., gpt-4-1106-preview, etc.
span.SetModelName(ctx, modelName)
span.SetTags(ctx, map[string]interface{}{
tracespec.CallOptions: tracespec.ModelCallOption{
Temperature: 0.6,
MaxTokens: int64(maxTokens),
TopP: 0.95,
N: 1,
PresencePenalty: util.Ptr(float32(1.0)),
FrequencyPenalty: util.Ptr(float32(1.0)),
},
})

return nil
}

type MyTransport struct {
Header http.Header
DefaultTransport http.RoundTripper
}

func (transport *MyTransport) RoundTrip(req *http.Request) (*http.Response, error) {
for key, values := range transport.Header {
for _, value := range values {
req.Header.Add(key, value)
}
}
return transport.DefaultTransport.RoundTrip(req)
}

func convertModelInput(messages []*entity.Message) *tracespec.ModelInput {
modelMessages := make([]*tracespec.ModelMessage, 0)
for _, message := range messages {
modelMessages = append(modelMessages, &tracespec.ModelMessage{
Role: string(message.Role),
Content: util.PtrValue(message.Content),
})
}

return &tracespec.ModelInput{
Messages: modelMessages,
}
}
9 changes: 9 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,27 @@ require (
github.com/bytedance/mockey v1.2.14
github.com/coze-dev/cozeloop-go/spec v0.1.0
github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/nikolalohinski/gonja/v2 v2.3.1
github.com/smartystreets/goconvey v1.8.1
github.com/valyala/fasttemplate v1.2.2
golang.org/x/sync v0.11.0
)

require (
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/gopherjs/gopherjs v1.17.2 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/jtolds/gls v4.20.0+incompatible // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/smarty/assertions v1.15.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
golang.org/x/arch v0.11.0 // indirect
golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 // indirect
golang.org/x/sys v0.26.0 // indirect
golang.org/x/text v0.14.0 // indirect
)

replace github.com/coze-dev/cozeloop-go/spec => ./spec
42 changes: 42 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,24 +1,66 @@
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw=
github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0=
github.com/bytedance/mockey v1.2.14 h1:KZaFgPdiUwW+jOWFieo3Lr7INM1P+6adO3hxZhDswY8=
github.com/bytedance/mockey v1.2.14/go.mod h1:1BPHF9sol5R1ud/+0VEHGQq/+i2lN+GTsr3O2Q9IENY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/nikolalohinski/gonja/v2 v2.3.1 h1:UGyLa6NDNq6dCGkFY33sziUssjTdh95xrYslxZdqNVU=
github.com/nikolalohinski/gonja/v2 v2.3.1/go.mod h1:1Wcc/5huTu6y36e0sOFR1XQoFlylw3c3H3L5WOz0RDg=
github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU=
github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY=
github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec=
github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY=
github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
golang.org/x/arch v0.11.0 h1:KXV8WWKCXm6tRpLirl2szsO5j/oOODwZf4hATmGVNs4=
golang.org/x/arch v0.11.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 h1:985EYyeCOxTpcgOTJpflJUwOeEz0CQOdPt73OzpE9F8=
golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
1 change: 1 addition & 0 deletions internal/consts/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ var (
ErrAuthInfoRequired = NewError("api token or jwt oauth info is required")
ErrParsePrivateKey = NewError("failed to parse private key")
ErrHeaderParent = NewError("header traceparent is illegal")
ErrTemplateRender = NewError("template render error")
)

type LoopError struct {
Expand Down
Loading