Skip to content

Commit 9ac4643

Browse files
committed
Initial chat schema
1 parent f2d24bc commit 9ac4643

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1969
-29
lines changed

cli/server.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2610,6 +2610,54 @@ func redirectHTTPToHTTPSDeprecation(ctx context.Context, logger slog.Logger, inv
26102610
}
26112611
}
26122612

2613+
func ReadAIProvidersFromEnv(environ []string) ([]codersdk.AIProviderConfig, error) {
2614+
// The index numbers must be in-order.
2615+
sort.Strings(environ)
2616+
2617+
var providers []codersdk.AIProviderConfig
2618+
for _, v := range serpent.ParseEnviron(environ, "CODER_AI_PROVIDER_") {
2619+
tokens := strings.SplitN(v.Name, "_", 2)
2620+
if len(tokens) != 2 {
2621+
return nil, xerrors.Errorf("invalid env var: %s", v.Name)
2622+
}
2623+
2624+
providerNum, err := strconv.Atoi(tokens[0])
2625+
if err != nil {
2626+
return nil, xerrors.Errorf("parse number: %s", v.Name)
2627+
}
2628+
2629+
var provider codersdk.AIProviderConfig
2630+
switch {
2631+
case len(providers) < providerNum:
2632+
return nil, xerrors.Errorf(
2633+
"provider num %v skipped: %s",
2634+
len(providers),
2635+
v.Name,
2636+
)
2637+
case len(providers) == providerNum:
2638+
// At the next next provider.
2639+
providers = append(providers, provider)
2640+
case len(providers) == providerNum+1:
2641+
// At the current provider.
2642+
provider = providers[providerNum]
2643+
}
2644+
2645+
key := tokens[1]
2646+
switch key {
2647+
case "TYPE":
2648+
provider.Type = v.Value
2649+
case "API_KEY":
2650+
provider.APIKey = v.Value
2651+
case "BASE_URL":
2652+
provider.BaseURL = v.Value
2653+
case "MODELS":
2654+
provider.Models = strings.Split(v.Value, " ")
2655+
}
2656+
providers[providerNum] = provider
2657+
}
2658+
return providers, nil
2659+
}
2660+
26132661
// ReadExternalAuthProvidersFromEnv is provided for compatibility purposes with
26142662
// the viper CLI.
26152663
func ReadExternalAuthProvidersFromEnv(environ []string) ([]codersdk.ExternalAuthConfig, error) {

cli/testdata/TestProvisioners_Golden/list.golden

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
ID CREATED AT LAST SEEN AT NAME VERSION TAGS KEY NAME STATUS CURRENT JOB ID CURRENT JOB STATUS PREVIOUS JOB ID PREVIOUS JOB STATUS ORGANIZATION
1+
ID CREATED AT LAST SEEN AT NAME VERSION TAGS KEY NAME STATUS CURRENT JOB ID CURRENT JOB STATUS PREVIOUS JOB ID PREVIOUS JOB STATUS ORGANIZATION
22
00000000-0000-0000-aaaa-000000000000 ====[timestamp]===== ====[timestamp]===== default-provisioner v0.0.0-devel map[owner: scope:organization] built-in idle <nil> <nil> 00000000-0000-0000-bbbb-000000000001 succeeded Coder
33
00000000-0000-0000-aaaa-000000000001 ====[timestamp]===== ====[timestamp]===== provisioner-1 v0.0.0 map[foo:bar owner: scope:organization] built-in busy 00000000-0000-0000-bbbb-000000000002 running <nil> <nil> Coder
44
00000000-0000-0000-aaaa-000000000002 ====[timestamp]===== ====[timestamp]===== provisioner-2 v0.0.0 map[owner: scope:organization] built-in offline <nil> <nil> 00000000-0000-0000-bbbb-000000000003 succeeded Coder
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
CREATED AT LAST SEEN AT KEY NAME NAME VERSION STATUS TAGS
1+
CREATED AT LAST SEEN AT KEY NAME NAME VERSION STATUS TAGS
22
====[timestamp]===== ====[timestamp]===== built-in test v0.0.0-devel idle map[owner: scope:organization]

cli/testdata/server-config.yaml.golden

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,10 @@ client:
519519
# Support links to display in the top right drop down menu.
520520
# (default: <unset>, type: struct[[]codersdk.LinkConfig])
521521
supportLinks: []
522+
# Configure AI providers.
523+
# (default: <unset>, type: struct[codersdk.AIConfig])
524+
ai:
525+
providers: []
522526
# External Authentication providers.
523527
# (default: <unset>, type: struct[[]codersdk.ExternalAuthConfig])
524528
externalAuthProviders: []

coderd/ai/ai.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package ai
2+
3+
import (
4+
"context"
5+
6+
"github.com/kylecarbs/aisdk-go"
7+
)
8+
9+
type Provider func(ctx context.Context, messages []aisdk.Message) (aisdk.DataStream, error)

coderd/apidoc/docs.go

Lines changed: 44 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/apidoc/swagger.json

Lines changed: 44 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/chat.go

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
package coderd
2+
3+
import (
4+
"encoding/json"
5+
"net/http"
6+
"time"
7+
8+
"github.com/coder/coder/v2/coderd/database"
9+
"github.com/coder/coder/v2/coderd/database/db2sdk"
10+
"github.com/coder/coder/v2/coderd/httpapi"
11+
"github.com/coder/coder/v2/coderd/httpmw"
12+
"github.com/coder/coder/v2/codersdk"
13+
"github.com/google/uuid"
14+
"github.com/kylecarbs/aisdk-go"
15+
)
16+
17+
// postChats creates a new chat.
18+
//
19+
// @Summary Create a chat
20+
// @ID post-chat
21+
// @Security CoderSessionToken
22+
// @Produce json
23+
// @Tags Chat
24+
// @Success 201 {object} codersdk.Chat
25+
// @Router /chats [post]
26+
func (api *API) postChats(w http.ResponseWriter, r *http.Request) {
27+
apiKey := httpmw.APIKey(r)
28+
ctx := r.Context()
29+
30+
chat, err := api.Database.InsertChat(ctx, database.InsertChatParams{
31+
ID: uuid.New(),
32+
OwnerID: apiKey.UserID,
33+
CreatedAt: time.Now(),
34+
UpdatedAt: time.Now(),
35+
Title: "New Chat",
36+
})
37+
if err != nil {
38+
httpapi.Write(ctx, w, http.StatusInternalServerError, codersdk.Response{
39+
Message: "Failed to create chat",
40+
Detail: err.Error(),
41+
})
42+
return
43+
}
44+
45+
httpapi.Write(ctx, w, http.StatusCreated, db2sdk.Chat(chat))
46+
}
47+
48+
// listChats lists all chats for a user.
49+
//
50+
// @Summary List chats
51+
// @ID list-chats
52+
// @Security CoderSessionToken
53+
// @Produce json
54+
// @Tags Chat
55+
// @Success 200 {array} codersdk.Chat
56+
// @Router /chats [get]
57+
func (api *API) listChats(w http.ResponseWriter, r *http.Request) {
58+
apiKey := httpmw.APIKey(r)
59+
ctx := r.Context()
60+
61+
chats, err := api.Database.GetChatsByOwnerID(ctx, apiKey.UserID)
62+
if err != nil {
63+
httpapi.Write(ctx, w, http.StatusInternalServerError, codersdk.Response{
64+
Message: "Failed to list chats",
65+
Detail: err.Error(),
66+
})
67+
}
68+
69+
httpapi.Write(ctx, w, http.StatusOK, db2sdk.Chats(chats))
70+
}
71+
72+
// chat returns a chat by ID.
73+
//
74+
// @Summary Get a chat
75+
// @ID get-chat
76+
// @Security CoderSessionToken
77+
// @Produce json
78+
// @Tags Chat
79+
// @Success 200 {object} codersdk.Chat
80+
// @Router /chats/{chat} [get]
81+
func (api *API) chat(w http.ResponseWriter, r *http.Request) {
82+
ctx := r.Context()
83+
chat := httpmw.ChatParam(r)
84+
httpapi.Write(ctx, w, http.StatusOK, db2sdk.Chat(chat))
85+
}
86+
87+
// chatMessages returns the messages of a chat.
88+
//
89+
// @Summary Get chat messages
90+
// @ID get-chat-messages
91+
// @Security CoderSessionToken
92+
// @Produce json
93+
// @Tags Chat
94+
// @Success 200 {array} aisdk.Message
95+
// @Router /chats/{chat}/messages [get]
96+
func (api *API) chatMessages(w http.ResponseWriter, r *http.Request) {
97+
ctx := r.Context()
98+
chat := httpmw.ChatParam(r)
99+
rawMessages, err := api.Database.GetChatMessagesByChatID(ctx, chat.ID)
100+
if err != nil {
101+
httpapi.Write(ctx, w, http.StatusInternalServerError, codersdk.Response{
102+
Message: "Failed to get chat messages",
103+
Detail: err.Error(),
104+
})
105+
}
106+
messages := make([]aisdk.Message, len(rawMessages))
107+
for i, message := range rawMessages {
108+
var msg aisdk.Message
109+
err = json.Unmarshal(message.Content, &msg)
110+
if err != nil {
111+
httpapi.Write(ctx, w, http.StatusInternalServerError, codersdk.Response{
112+
Message: "Failed to unmarshal chat message",
113+
Detail: err.Error(),
114+
})
115+
}
116+
messages[i] = msg
117+
}
118+
119+
httpapi.Write(ctx, w, http.StatusOK, messages)
120+
}
121+
122+
func (api *API) postChatMessage(w http.ResponseWriter, r *http.Request) {
123+
ctx := r.Context()
124+
chat := httpmw.ChatParam(r)
125+
var message aisdk.Message
126+
err := json.NewDecoder(r.Body).Decode(&message)
127+
if err != nil {
128+
httpapi.Write(ctx, w, http.StatusBadRequest, codersdk.Response{
129+
Message: "Failed to decode chat message",
130+
Detail: err.Error(),
131+
})
132+
}
133+
134+
var stream aisdk.DataStream
135+
stream.WithToolCalling(func(toolCall aisdk.ToolCall) any {
136+
return nil
137+
})
138+
}

coderd/coderd.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -974,6 +974,16 @@ func New(options *Options) *API {
974974
r.Get("/{fileID}", api.fileByID)
975975
r.Post("/", api.postFile)
976976
})
977+
r.Route("/chats", func(r chi.Router) {
978+
r.Use(apiKeyMiddleware)
979+
r.Get("/", api.listChats)
980+
r.Post("/", api.postChats)
981+
r.Route("/{chat}", func(r chi.Router) {
982+
r.Use(httpmw.ExtractChatParam(options.Database))
983+
r.Get("/", api.chat)
984+
r.Get("/messages", api.chatMessages)
985+
})
986+
})
977987
r.Route("/external-auth", func(r chi.Router) {
978988
r.Use(
979989
apiKeyMiddleware,

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy