-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Expand file tree
/
Copy pathexp_scaletest_chat_test.go
More file actions
141 lines (124 loc) · 4.21 KB
/
Copy pathexp_scaletest_chat_test.go
File metadata and controls
141 lines (124 loc) · 4.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//go:build !slim
package cli_test
import (
"bytes"
"context"
"io"
"strings"
"testing"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
"cdr.dev/slog/v3"
"cdr.dev/slog/v3/sloggers/sloghuman"
"github.com/coder/coder/v2/cli/clitest"
"github.com/coder/coder/v2/coderd/aibridgedtest"
"github.com/coder/coder/v2/coderd/coderdtest"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/scaletest/llmmock"
"github.com/coder/coder/v2/testutil"
)
const scaletestChatPrompt = "Reply with one short sentence from the scaletest."
func TestScaleTestChat(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitLong)
values := coderdtest.DeploymentValues(t, func(dv *codersdk.DeploymentValues) {
require.NoError(t, dv.AI.BridgeConfig.Enabled.Set("true"))
})
client, _, api := coderdtest.NewWithAPI(t, &coderdtest.Options{
DeploymentValues: values,
})
aibridgedtest.StartTestAIBridgeDaemon(t.Context(), t, api, nil)
coderdtest.CreateFirstUser(t, client)
server := new(llmmock.Server)
require.NoError(t, server.Start(context.Background(), llmmock.Config{
Address: "127.0.0.1:0",
Logger: slog.Make(sloghuman.Sink(io.Discard)).Leveled(slog.LevelDebug),
}))
t.Cleanup(func() {
require.NoError(t, server.Stop())
})
mockURL := server.APIAddress() + "/v1"
inv, root := clitest.New(t,
"exp", "scaletest", "chat",
"--chats-per-workspace", "1",
"--turns", "1",
"--prompt", scaletestChatPrompt,
"--timeout", "30s",
"--job-timeout", "30s",
"--cleanup-timeout", "30s",
"--cleanup-job-timeout", "30s",
"--scaletest-prometheus-address", "127.0.0.1:0",
"--scaletest-prometheus-wait", "0s",
"--provider-propagation-wait", "10ms",
"--llm-mock-url", mockURL,
)
//nolint:gocritic // The scaletest chat command requires an admin client.
clitest.SetupConfig(t, client, root)
var stderr bytes.Buffer
inv.Stdout = io.Discard
inv.Stderr = &stderr
err := inv.WithContext(ctx).Run()
require.NoError(t, err, stderr.String())
require.Contains(t, stderr.String(), "Scale test passed: 1/1 runs succeeded")
provider, err := client.AIProvider(ctx, "coder-scaletest-mock")
require.NoError(t, err)
require.Equal(t, mockURL, provider.BaseURL)
defaultOrg, err := client.OrganizationByName(ctx, codersdk.DefaultOrganization)
require.NoError(t, err)
configs, err := client.ChatModels(ctx, defaultOrg.ID)
require.NoError(t, err)
matchingConfigs := scaletestModelConfigsForProvider(configs.Models, provider.ID)
require.Len(t, matchingConfigs, 1)
require.True(t, matchingConfigs[0].Enabled)
chats, err := client.ListChats(ctx, &codersdk.ListChatsOptions{Query: "archived:true"})
require.NoError(t, err)
var scaletestMessages []codersdk.ChatMessage
for _, chat := range chats {
resp, err := client.GetChatMessages(ctx, chat.ID, nil)
require.NoError(t, err)
if userText, ok := chatMessageText(resp.Messages, codersdk.ChatMessageRoleUser); ok &&
strings.Contains(userText, scaletestChatPrompt) {
scaletestMessages = resp.Messages
break
}
}
require.NotEmpty(t, scaletestMessages)
assistantText, ok := chatMessageText(scaletestMessages, codersdk.ChatMessageRoleAssistant)
require.True(t, ok, "expected an assistant reply in the scaletest chat")
require.NotEmpty(t, assistantText)
}
// chatMessageText concatenates the text parts of every message with the given
// role, reporting whether any such message was found. It aggregates across
// messages because the API returns them newest-first and a turn can produce
// more than one message per role.
func chatMessageText(messages []codersdk.ChatMessage, role codersdk.ChatMessageRole) (string, bool) {
var (
b strings.Builder
found bool
)
for _, msg := range messages {
if msg.Role != role {
continue
}
found = true
for _, part := range msg.Content {
if part.Type == codersdk.ChatMessagePartTypeText {
_, _ = b.WriteString(part.Text)
}
}
}
return b.String(), found
}
func scaletestModelConfigsForProvider(configs []codersdk.ChatModel, providerID uuid.UUID) []codersdk.ChatModel {
matches := make([]codersdk.ChatModel, 0, 1)
for _, config := range configs {
if config.AIProviderID != providerID {
continue
}
if config.Model != "scaletest-model" {
continue
}
matches = append(matches, config)
}
return matches
}