Skip to content

Commit c09a101

Browse files
committed
broken back of fag packet code for langwatch go intergration
1 parent d5af5e5 commit c09a101

File tree

14 files changed

+521
-15
lines changed

14 files changed

+521
-15
lines changed

libraries/langwatch/README.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# langwatch-go
2+
3+
This is a Go client for the [Langwatch 🏰](https://langwatch.ai) API.
4+
5+
## Feature set
6+
7+
| Feature name | Supported |
8+
| ------------ | --------- |
9+
| LLM Spans | Partial |
10+
| RAG Spans | 🚫 |
11+
12+
## Usage
13+
14+
```go
15+
package main
16+
17+
import (
18+
"github.com/0xdeafcafe/bloefish/libraries/langwatch"
19+
"github.com/0xdeafcafe/bloefish/libraries/ksuid"
20+
"github.com/0xdeafcafe/bloefish/libraries/openai"
21+
)
22+
23+
var client langwatch.Client
24+
var oaiClient openai.MockClient
25+
26+
func init() {
27+
ctx := context.Background()
28+
client = langwatch.NewClient()
29+
oaiClient = openai.NewMockClient()
30+
31+
threadID := ksuid.Generate(ctx, "thread").String()
32+
userID := ksuid.Generate(ctx, "user").String()
33+
34+
// Create a trace
35+
ctx, trace := client.CreateTrace(ctx, threadID, langwatch.WithTraceMetadata(
36+
langwatch.Attribute("user_id", userID),
37+
langwatch.Attribute("app_id", "AppID"),
38+
langwatch.Attribute("app_version", "1.0.0"),
39+
))
40+
defer trace.End()
41+
42+
messages := createOpenAIMessages()
43+
44+
ctx, span := trace.StartLLMSpan(ctx, langwatch.StartLLMSpanCommand{
45+
Name: "llm",
46+
Model: "gpt-4.0",
47+
Messages: langwatch.CoerceOpenAIMessages(messages),
48+
// Messages: langwatch.CoerceGrokMessages(messages),
49+
// Messages: langwatch.CoerceDeepseekMessages(messages),
50+
})
51+
defer span.End()
52+
53+
span.AddOutput(ctx, langwatch.LLMSpanOutput{
54+
Output:
55+
})
56+
57+
// Create a span
58+
59+
60+
}
61+
```

libraries/langwatch/attirbute.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package langwatch
2+
3+
const (
4+
AttributeNameUserID = "user_id"
5+
AttributeNameThreadID = "thread_id"
6+
AttributeNameCustomerID = "customer_id"
7+
)
8+
9+
type attribute[T any] struct {
10+
Name string
11+
Value T
12+
}
13+
14+
func Attribute[T any](name string, value T) attribute[any] {
15+
return attribute[any]{
16+
Name: name,
17+
Value: value,
18+
}
19+
}

libraries/langwatch/client.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package langwatch
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"encoding/json"
7+
"fmt"
8+
"net/http"
9+
"os"
10+
)
11+
12+
const (
13+
defaultEndpointURL = "https://app.langwatch.ai"
14+
defaultAPIKeyEnvironmentVariableName = "LANGWATCH_API_KEY"
15+
)
16+
17+
type client struct {
18+
endpointURL string
19+
apiKey string
20+
httpClient *http.Client
21+
}
22+
23+
type Client interface {
24+
CreateTrace(ctx context.Context, threadID string, opts ...TraceOption) (context.Context, Trace)
25+
}
26+
27+
func NewClient(opts ...ClientOption) Client {
28+
c := &client{
29+
endpointURL: defaultEndpointURL,
30+
apiKey: os.Getenv(defaultAPIKeyEnvironmentVariableName),
31+
httpClient: &http.Client{},
32+
}
33+
34+
for _, opt := range opts {
35+
opt(c)
36+
}
37+
38+
return c
39+
}
40+
41+
func (c *client) Collect(ctx context.Context, body interface{}) error {
42+
jsonBytes, err := json.Marshal(body)
43+
if err != nil {
44+
return fmt.Errorf("failed to marshal collector request: %w", err)
45+
}
46+
47+
req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.endpointURL+"/api/collector", bytes.NewReader(jsonBytes))
48+
if err != nil {
49+
return fmt.Errorf("failed to create collector request: %w", err)
50+
}
51+
52+
resp, err := c.httpClient.Do(req)
53+
if err != nil {
54+
return fmt.Errorf("failed to make collector request: %w", err)
55+
}
56+
57+
if resp.StatusCode != http.StatusNoContent {
58+
return fmt.Errorf("failed to successfully make collector request: %w", err)
59+
}
60+
61+
return nil
62+
}

libraries/langwatch/context.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
package langwatch

libraries/langwatch/langwatch.go

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

libraries/langwatch/noop.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package langwatch
2+
3+
import "context"
4+
5+
type noopClient struct{}
6+
type noopTrace struct{}
7+
type noopSpanLLM struct{}
8+
9+
func NewNoopClient() Client {
10+
return &noopClient{}
11+
}
12+
13+
func (c *noopClient) CreateTrace(ctx context.Context, theadID string, _ ...TraceOption) (context.Context, Trace) {
14+
t := &noopTrace{}
15+
16+
return ctx, t
17+
}
18+
19+
func (t *noopTrace) StartLLMSpan(ctx context.Context, _ StartLLMSpanParams) (context.Context, SpanLLM) {
20+
return ctx, &noopSpanLLM{}
21+
}
22+
23+
func (s *noopSpanLLM) AddOutput(ctx context.Context) {}
24+
func (s *noopSpanLLM) End(ctx context.Context) {}

libraries/langwatch/options.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package langwatch
2+
3+
import (
4+
"net/http"
5+
"os"
6+
)
7+
8+
type ClientOption func(*client)
9+
type TraceOption func(*trace)
10+
11+
// WithEndpointURL sets a custom endpoint URL for the client. By default
12+
// `defaultEndpointURL` is used.
13+
func WithEndpointURL(endpoint string) ClientOption {
14+
return func(c *client) {
15+
c.endpointURL = endpoint
16+
}
17+
}
18+
19+
// WithAPIKey sets the API key for the client.
20+
func WithAPIKey(apiKey string) ClientOption {
21+
return func(c *client) {
22+
c.apiKey = apiKey
23+
}
24+
}
25+
26+
// WithEnvironmentApiKey sets the API key for the client from an environment variable.
27+
func WithEnvironmentApiKey(environmentVariable string) ClientOption {
28+
return func(c *client) {
29+
c.apiKey = os.Getenv(environmentVariable)
30+
}
31+
}
32+
33+
// WithHTTPClient sets the http client for the client.
34+
func WithHTTPClient(httpClient *http.Client) ClientOption {
35+
return func(c *client) {
36+
c.httpClient = httpClient
37+
}
38+
}
39+
40+
// WithTraceMetadata sets attributes on a trace to attach as metadata.
41+
func WithTraceMetadata(attributes ...attribute[any]) TraceOption {
42+
return func(t *trace) {
43+
t.attributes = attributes
44+
}
45+
}

libraries/langwatch/span_llm.go

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
package langwatch
2+
3+
import (
4+
"context"
5+
"time"
6+
7+
"github.com/0xdeafcafe/bloefish/libraries/ptr"
8+
)
9+
10+
type SpanLLM interface {
11+
AddOutput(ctx context.Context, output string)
12+
AddError(ctx context.Context, err error)
13+
End(ctx context.Context)
14+
}
15+
16+
type LLMSpanInputChatMessage struct {
17+
Role string `json:"role"`
18+
Message string `json:"message"`
19+
}
20+
21+
type LLMSpanOutputChatMessage struct {
22+
Role string `json:"role"`
23+
Message string `json:"message"`
24+
FunctionCall interface{} `json:"function_call"`
25+
ToolCalls []interface{} `json:"tool_calls"`
26+
}
27+
28+
type StartLLMSpanParams struct {
29+
Vendor string
30+
Model string
31+
Name *string
32+
Input StartLLMSpanChatMessageInput
33+
}
34+
35+
type StartLLMSpanChatMessageInput struct {
36+
Messages []LLMSpanInputChatMessage
37+
}
38+
39+
type spanLLM struct {
40+
client *client
41+
trace Trace
42+
spanID string
43+
name *string
44+
vendor *string
45+
model *string
46+
input *spanLLMInput
47+
output *spanLLMOutput
48+
error error
49+
timestamps *spanLLMTimestamps
50+
metrics *spanLLMMetrics
51+
}
52+
53+
type spanLLMInput struct {
54+
Type string `json:"type"`
55+
Value interface{} `json:"value"`
56+
}
57+
type spanLLMOutput struct {
58+
Type string `json:"type"`
59+
Value interface{} `json:"value"`
60+
}
61+
type spanLLMParams struct {
62+
Temperature *float64 `json:"temperature,omitempty"`
63+
Stream *bool `json:"stream,omitempty"`
64+
}
65+
type spanLLMTimestamps struct {
66+
StartedAt *int64 `json:"started_at,omitempty"`
67+
FinishedAt *int64 `json:"finished_at,omitempty"`
68+
}
69+
type spanLLMMetrics struct {
70+
PromptTokens *int `json:"prompt_tokens,omitempty"`
71+
CompletionTokens *int `json:"completion_tokens,omitempty"`
72+
}
73+
74+
type spanCollectorRequest struct {
75+
TraceID string `json:"trace_id"`
76+
Spans []*spanCollectorRequestSpan `json:"spans"`
77+
Metadata map[string]string `json:"metadata"`
78+
}
79+
type spanCollectorRequestSpan struct {
80+
Type string
81+
SpanID string
82+
Vendor string
83+
Model string
84+
Input interface{}
85+
Output interface{}
86+
Params interface{}
87+
Metrics interface{}
88+
Timestamps interface{}
89+
}
90+
type spanCollectorRequestMetadata struct {
91+
}
92+
93+
func (t *trace) StartLLMSpan(ctx context.Context, spanID string, params StartLLMSpanParams) (context.Context, SpanLLM) {
94+
return ctx, &spanLLM{
95+
client: t.client,
96+
trace: t,
97+
98+
spanID: spanID,
99+
vendor: &params.Vendor,
100+
model: &params.Model,
101+
102+
input: &spanLLMInput{
103+
Type: "chat_messages",
104+
Value: params.Input.Messages,
105+
},
106+
107+
timestamps: &spanLLMTimestamps{
108+
StartedAt: ptr.P(time.Now().UnixNano()),
109+
},
110+
}
111+
}
112+
113+
func (s *spanLLM) AddOutput(ctx context.Context, output string) {
114+
s.output = &spanLLMOutput{
115+
Type: "chat_messages",
116+
Value: []LLMSpanOutputChatMessage{{
117+
Role: "bot",
118+
Message: output,
119+
FunctionCall: nil,
120+
ToolCalls: []interface{}{},
121+
}},
122+
}
123+
s.timestamps.FinishedAt = ptr.P(time.Now().UnixNano())
124+
}
125+
126+
func (s *spanLLM) AddError(ctx context.Context, err error) {
127+
s.error = err
128+
}
129+
130+
func (s *spanLLM) End(ctx context.Context) {
131+
s.client.Collect(ctx)
132+
}

libraries/langwatch/trace.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package langwatch
2+
3+
import "context"
4+
5+
type Trace interface {
6+
StartLLMSpan(ctx context.Context, spanID string, params StartLLMSpanParams) (context.Context, SpanLLM)
7+
}
8+
9+
type trace struct {
10+
attributes []attribute[any]
11+
client *client
12+
}
13+
14+
func (c *client) CreateTrace(ctx context.Context, theadID string, opts ...TraceOption) (context.Context, Trace) {
15+
t := &trace{
16+
attributes: []attribute[any]{},
17+
client: c,
18+
}
19+
20+
for _, opt := range opts {
21+
opt(t)
22+
}
23+
24+
return ctx, t
25+
}

0 commit comments

Comments
 (0)