Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

chore(coderd): update aitasks.go to leverage agentapi-sdk-go#20159

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
johnstcn merged 2 commits intomainfromcj/agentapi-sdk-go
Oct 3, 2025
Merged
Changes fromall 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
168 changes: 27 additions & 141 deletionscoderd/aitasks.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
package coderd

import (
"bytes"
"context"
"database/sql"
"encoding/json"
"errors"
"fmt"
"io"
"net"
"net/http"
"net/url"
"path"
"slices"
"strings"
"time"
Expand All@@ -31,6 +27,8 @@ import (
"github.com/coder/coder/v2/coderd/taskname"
"github.com/coder/coder/v2/coderd/util/slice"
"github.com/coder/coder/v2/codersdk"

aiagentapi "github.com/coder/agentapi-sdk-go"
)

// This endpoint is experimental and not guaranteed to be stable, so we're not
Expand DownExpand Up@@ -629,61 +627,37 @@ func (api *API) taskSend(rw http.ResponseWriter, r *http.Request) {
}

if err = api.authAndDoWithTaskSidebarAppClient(r, taskID, func(ctx context.Context, client *http.Client, appURL *url.URL) error {
status, err :=agentapiDoStatusRequest(ctx,client, appURL)
agentAPIClient, err :=aiagentapi.NewClient(appURL.String(), aiagentapi.WithHTTPClient(client))
if err != nil {
return err
}

if status != "stable" {
return httperror.NewResponseError(http.StatusBadGateway, codersdk.Response{
Message: "Task app is not readytoaccept input.",
Detail:fmt.Sprintf("Status: %s", status),
Message: "Failedtocreate agentapi client.",
Detail:err.Error(),
})
}

var reqBody struct {
Content string `json:"content"`
Type string `json:"type"`
}
reqBody.Content = req.Input
reqBody.Type = "user"

req, err := agentapiNewRequest(ctx, http.MethodPost, appURL, "message", reqBody)
if err != nil {
return err
}

resp, err := client.Do(req)
statusResp, err := agentAPIClient.GetStatus(ctx)
if err != nil {
return httperror.NewResponseError(http.StatusBadGateway, codersdk.Response{
Message: "Failed toreachtask app endpoint.",
Message: "Failed toget status fromtask app.",
Detail: err.Error(),
})
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(io.LimitReader(resp.Body, 128))
return httperror.NewResponseError(http.StatusBadGateway, codersdk.Response{
Message: "Task app rejected the message.",
Detail: fmt.Sprintf("Upstream status: %d; Body: %s", resp.StatusCode, body),
})
}

// {"$schema":"http://localhost:3284/schemas/MessageResponseBody.json","ok":true}
// {"$schema":"http://localhost:3284/schemas/ErrorModel.json","title":"Unprocessable Entity","status":422,"detail":"validation failed","errors":[{"location":"body.type","value":"oof"}]}
var respBody map[string]any
if err := json.NewDecoder(resp.Body).Decode(&respBody); err != nil {
if statusResp.Status != aiagentapi.StatusStable {
return httperror.NewResponseError(http.StatusBadGateway, codersdk.Response{
Message: "Failed to decode task app response body.",
Detail:err.Error(),
Message: "Task app is not ready to accept input.",
Detail:fmt.Sprintf("Status: %s", statusResp.Status),
})
}

if v, ok := respBody["ok"].(bool); !ok || !v {
_, err = agentAPIClient.PostMessage(ctx, aiagentapi.PostMessageParams{
Content: req.Input,
Type: aiagentapi.MessageTypeUser,
})
if err != nil {
return httperror.NewResponseError(http.StatusBadGateway, codersdk.Response{
Message: "Task app rejected the message.",
Detail:fmt.Sprintf("Upstream response: %v", respBody),
Detail:err.Error(),
})
}

Expand All@@ -710,51 +684,29 @@ func (api *API) taskLogs(rw http.ResponseWriter, r *http.Request) {

var out codersdk.TaskLogsResponse
if err := api.authAndDoWithTaskSidebarAppClient(r, taskID, func(ctx context.Context, client *http.Client, appURL *url.URL) error {
req, err := agentapiNewRequest(ctx, http.MethodGet, appURL, "messages", nil)
if err != nil {
return err
}

resp, err := client.Do(req)
agentAPIClient, err := aiagentapi.NewClient(appURL.String(), aiagentapi.WithHTTPClient(client))
if err != nil {
return httperror.NewResponseError(http.StatusBadGateway, codersdk.Response{
Message: "Failed toreach task app endpoint.",
Message: "Failed tocreate agentapi client.",
Detail: err.Error(),
})
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(io.LimitReader(resp.Body, 128))
return httperror.NewResponseError(http.StatusBadGateway, codersdk.Response{
Message: "Task app rejected the request.",
Detail: fmt.Sprintf("Upstream status: %d; Body: %s", resp.StatusCode, body),
})
}

// {"$schema":"http://localhost:3284/schemas/MessagesResponseBody.json","messages":[]}
var respBody struct {
Messages []struct {
ID int `json:"id"`
Content string `json:"content"`
Role string `json:"role"`
Time time.Time `json:"time"`
} `json:"messages"`
}
if err := json.NewDecoder(resp.Body).Decode(&respBody); err != nil {
messagesResp, err := agentAPIClient.GetMessages(ctx)
if err != nil {
return httperror.NewResponseError(http.StatusBadGateway, codersdk.Response{
Message: "Failed todecode task app response body.",
Message: "Failed toget messages from task app.",
Detail: err.Error(),
})
}

logs := make([]codersdk.TaskLogEntry, 0, len(respBody.Messages))
for _, m := rangerespBody.Messages {
logs := make([]codersdk.TaskLogEntry, 0, len(messagesResp.Messages))
for _, m := rangemessagesResp.Messages {
var typ codersdk.TaskLogType
switchstrings.ToLower(m.Role) {
case"user":
switch m.Role {
caseaiagentapi.RoleUser:
typ = codersdk.TaskLogTypeInput
case"agent":
caseaiagentapi.RoleAgent:
typ = codersdk.TaskLogTypeOutput
default:
return httperror.NewResponseError(http.StatusBadGateway, codersdk.Response{
Expand All@@ -763,7 +715,7 @@ func (api *API) taskLogs(rw http.ResponseWriter, r *http.Request) {
})
}
logs = append(logs, codersdk.TaskLogEntry{
ID:m.ID,
ID:int(m.Id),
Content: m.Content,
Type: typ,
Time: m.Time,
Expand DownExpand Up@@ -903,69 +855,3 @@ func (api *API) authAndDoWithTaskSidebarAppClient(
}
return do(ctx, client, parsedURL)
}

func agentapiNewRequest(ctx context.Context, method string, appURL *url.URL, appURLPath string, body any) (*http.Request, error) {
u := *appURL
u.Path = path.Join(appURL.Path, appURLPath)

var bodyReader io.Reader
if body != nil {
b, err := json.Marshal(body)
if err != nil {
return nil, httperror.NewResponseError(http.StatusBadRequest, codersdk.Response{
Message: "Failed to marshal task app request body.",
Detail: err.Error(),
})
}
bodyReader = bytes.NewReader(b)
}

req, err := http.NewRequestWithContext(ctx, method, u.String(), bodyReader)
if err != nil {
return nil, httperror.NewResponseError(http.StatusBadRequest, codersdk.Response{
Message: "Failed to create task app request.",
Detail: err.Error(),
})
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")

return req, nil
}

func agentapiDoStatusRequest(ctx context.Context, client *http.Client, appURL *url.URL) (string, error) {
req, err := agentapiNewRequest(ctx, http.MethodGet, appURL, "status", nil)
if err != nil {
return "", err
}

resp, err := client.Do(req)
if err != nil {
return "", httperror.NewResponseError(http.StatusBadGateway, codersdk.Response{
Message: "Failed to reach task app endpoint.",
Detail: err.Error(),
})
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return "", httperror.NewResponseError(http.StatusBadGateway, codersdk.Response{
Message: "Task app status returned an error.",
Detail: fmt.Sprintf("Status code: %d", resp.StatusCode),
})
}

// {"$schema":"http://localhost:3284/schemas/StatusResponseBody.json","status":"stable"}
var respBody struct {
Status string `json:"status"`
}

if err := json.NewDecoder(resp.Body).Decode(&respBody); err != nil {
return "", httperror.NewResponseError(http.StatusBadGateway, codersdk.Response{
Message: "Failed to decode task app status response body.",
Detail: err.Error(),
})
}

return respBody.Status, nil
}
Loading

[8]ページ先頭

©2009-2025 Movatter.jp