Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
Initial chat schema
  • Loading branch information
kylecarbs authored and johnstcn committed May 2, 2025
commit f762a7645c060f602294ae027d019783c8ac98e4
48 changes: 48 additions & 0 deletions cli/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -2621,6 +2621,54 @@ func redirectHTTPToHTTPSDeprecation(ctx context.Context, logger slog.Logger, inv
}
}

func ReadAIProvidersFromEnv(environ []string) ([]codersdk.AIProviderConfig, error) {
// The index numbers must be in-order.
sort.Strings(environ)

var providers []codersdk.AIProviderConfig
for _, v := range serpent.ParseEnviron(environ, "CODER_AI_PROVIDER_") {
tokens := strings.SplitN(v.Name, "_", 2)
if len(tokens) != 2 {
return nil, xerrors.Errorf("invalid env var: %s", v.Name)
}

providerNum, err := strconv.Atoi(tokens[0])
if err != nil {
return nil, xerrors.Errorf("parse number: %s", v.Name)
}

var provider codersdk.AIProviderConfig
switch {
case len(providers) < providerNum:
return nil, xerrors.Errorf(
"provider num %v skipped: %s",
len(providers),
v.Name,
)
case len(providers) == providerNum:
// At the next next provider.
providers = append(providers, provider)
case len(providers) == providerNum+1:
// At the current provider.
provider = providers[providerNum]
}

key := tokens[1]
switch key {
case "TYPE":
provider.Type = v.Value
case "API_KEY":
provider.APIKey = v.Value
case "BASE_URL":
provider.BaseURL = v.Value
case "MODELS":
provider.Models = strings.Split(v.Value, " ")
}
providers[providerNum] = provider
}
return providers, nil
}

// ReadExternalAuthProvidersFromEnv is provided for compatibility purposes with
// the viper CLI.
func ReadExternalAuthProvidersFromEnv(environ []string) ([]codersdk.ExternalAuthConfig, error) {
Expand Down
2 changes: 1 addition & 1 deletion cli/testdata/TestProvisioners_Golden/list.golden
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
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
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
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
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
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
Expand Down
2 changes: 1 addition & 1 deletion cli/testdata/coder_provisioner_list.golden
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
CREATED AT LAST SEEN AT KEY NAME NAME VERSION STATUS TAGS
CREATED AT LAST SEEN AT KEY NAME NAME VERSION STATUS TAGS
====[timestamp]===== ====[timestamp]===== built-in test v0.0.0-devel idle map[owner: scope:organization]
4 changes: 4 additions & 0 deletions cli/testdata/server-config.yaml.golden
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,10 @@ client:
# Support links to display in the top right drop down menu.
# (default: <unset>, type: struct[[]codersdk.LinkConfig])
supportLinks: []
# Configure AI providers.
# (default: <unset>, type: struct[codersdk.AIConfig])
ai:
providers: []
# External Authentication providers.
# (default: <unset>, type: struct[[]codersdk.ExternalAuthConfig])
externalAuthProviders: []
Expand Down
9 changes: 9 additions & 0 deletions coderd/ai/ai.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package ai

import (
"context"

"github.com/kylecarbs/aisdk-go"
)

type Provider func(ctx context.Context, messages []aisdk.Message) (aisdk.DataStream, error)
44 changes: 44 additions & 0 deletions coderd/apidoc/docs.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

44 changes: 44 additions & 0 deletions coderd/apidoc/swagger.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

138 changes: 138 additions & 0 deletions coderd/chat.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package coderd

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

"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/database/db2sdk"
"github.com/coder/coder/v2/coderd/httpapi"
"github.com/coder/coder/v2/coderd/httpmw"
"github.com/coder/coder/v2/codersdk"
"github.com/google/uuid"
"github.com/kylecarbs/aisdk-go"
)

// postChats creates a new chat.
//
// @Summary Create a chat
// @ID post-chat
// @Security CoderSessionToken
// @Produce json
// @Tags Chat
// @Success 201 {object} codersdk.Chat
// @Router /chats [post]
func (api *API) postChats(w http.ResponseWriter, r *http.Request) {
apiKey := httpmw.APIKey(r)
ctx := r.Context()

chat, err := api.Database.InsertChat(ctx, database.InsertChatParams{
ID: uuid.New(),
OwnerID: apiKey.UserID,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
Title: "New Chat",
})
if err != nil {
httpapi.Write(ctx, w, http.StatusInternalServerError, codersdk.Response{
Message: "Failed to create chat",
Detail: err.Error(),
})
return
}

httpapi.Write(ctx, w, http.StatusCreated, db2sdk.Chat(chat))
}

// listChats lists all chats for a user.
//
// @Summary List chats
// @ID list-chats
// @Security CoderSessionToken
// @Produce json
// @Tags Chat
// @Success 200 {array} codersdk.Chat
// @Router /chats [get]
func (api *API) listChats(w http.ResponseWriter, r *http.Request) {
apiKey := httpmw.APIKey(r)
ctx := r.Context()

chats, err := api.Database.GetChatsByOwnerID(ctx, apiKey.UserID)
if err != nil {
httpapi.Write(ctx, w, http.StatusInternalServerError, codersdk.Response{
Message: "Failed to list chats",
Detail: err.Error(),
})
}

httpapi.Write(ctx, w, http.StatusOK, db2sdk.Chats(chats))
}

// chat returns a chat by ID.
//
// @Summary Get a chat
// @ID get-chat
// @Security CoderSessionToken
// @Produce json
// @Tags Chat
// @Success 200 {object} codersdk.Chat
// @Router /chats/{chat} [get]
func (api *API) chat(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
chat := httpmw.ChatParam(r)
httpapi.Write(ctx, w, http.StatusOK, db2sdk.Chat(chat))
}

// chatMessages returns the messages of a chat.
//
// @Summary Get chat messages
// @ID get-chat-messages
// @Security CoderSessionToken
// @Produce json
// @Tags Chat
// @Success 200 {array} aisdk.Message
// @Router /chats/{chat}/messages [get]
func (api *API) chatMessages(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
chat := httpmw.ChatParam(r)
rawMessages, err := api.Database.GetChatMessagesByChatID(ctx, chat.ID)
if err != nil {
httpapi.Write(ctx, w, http.StatusInternalServerError, codersdk.Response{
Message: "Failed to get chat messages",
Detail: err.Error(),
})
}
messages := make([]aisdk.Message, len(rawMessages))
for i, message := range rawMessages {
var msg aisdk.Message
err = json.Unmarshal(message.Content, &msg)
if err != nil {
httpapi.Write(ctx, w, http.StatusInternalServerError, codersdk.Response{
Message: "Failed to unmarshal chat message",
Detail: err.Error(),
})
}
messages[i] = msg
}

httpapi.Write(ctx, w, http.StatusOK, messages)
}

func (api *API) postChatMessage(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
chat := httpmw.ChatParam(r)
var message aisdk.Message
err := json.NewDecoder(r.Body).Decode(&message)
if err != nil {
httpapi.Write(ctx, w, http.StatusBadRequest, codersdk.Response{
Message: "Failed to decode chat message",
Detail: err.Error(),
})
}

var stream aisdk.DataStream
stream.WithToolCalling(func(toolCall aisdk.ToolCall) any {
return nil
})
}
10 changes: 10 additions & 0 deletions coderd/coderd.go
Original file line number Diff line number Diff line change
Expand Up @@ -998,6 +998,16 @@ func New(options *Options) *API {
r.Get("/{fileID}", api.fileByID)
r.Post("/", api.postFile)
})
r.Route("/chats", func(r chi.Router) {
r.Use(apiKeyMiddleware)
r.Get("/", api.listChats)
r.Post("/", api.postChats)
r.Route("/{chat}", func(r chi.Router) {
r.Use(httpmw.ExtractChatParam(options.Database))
r.Get("/", api.chat)
r.Get("/messages", api.chatMessages)
})
})
r.Route("/external-auth", func(r chi.Router) {
r.Use(
apiKeyMiddleware,
Expand Down
Loading