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

Commit91755c2

Browse files
committed
WIP
Signed-off-by: Danny Kopping <dannykopping@gmail.com>
1 parent667374f commit91755c2

File tree

15 files changed

+291
-5
lines changed

15 files changed

+291
-5
lines changed

‎aibridged/aibridged.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"golang.org/x/xerrors"
1616

1717
"github.com/coder/coder/v2/aibridged/proto"
18+
"github.com/coder/coder/v2/coderd/database"
1819
"github.com/coder/coder/v2/codersdk"
1920
)
2021

@@ -51,13 +52,13 @@ type Server struct {
5152
bridge*Bridge
5253
}
5354

54-
funcNew(rpcDialerDialer,httpAddrstring,logger slog.Logger) (*Server,error) {
55+
funcNew(store database.Store,rpcDialerDialer,httpAddrstring,logger slog.Logger) (*Server,error) {
5556
ifrpcDialer==nil {
5657
returnnil,xerrors.Errorf("nil rpcDialer given")
5758
}
5859

5960
ctx,cancel:=context.WithCancel(context.Background())
60-
bridge:=NewBridge(httpAddr)
61+
bridge:=NewBridge(httpAddr,store)
6162
daemon:=&Server{
6263
logger:logger,
6364
clientDialer:rpcDialer,

‎aibridged/bridge.go

Lines changed: 200 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,208 @@ package aibridged
22

33
import (
44
"fmt"
5+
"log/slog"
56
"net"
67
"net/http"
78
"net/http/httputil"
89
"net/url"
910
"os"
1011
"strings"
12+
"time"
1113

14+
"github.com/charmbracelet/log"
15+
"github.com/coder/freeway/apibridge"
16+
"github.com/coder/freeway/middleware"
17+
"github.com/coder/freeway/middleware/logger"
18+
"github.com/coder/freeway/middleware/provider/anthropic"
19+
"github.com/coder/freeway/middleware/provider/openai"
20+
"github.com/coder/freeway/server"
1221
"golang.org/x/xerrors"
22+
23+
"github.com/coder/coder/v2/coderd/database"
1324
)
1425

1526
typeBridgestruct {
1627
httpSrv*http.Server
1728
addrstring
1829
}
1930

20-
funcNewBridge(addrstring)*Bridge {
31+
funcNewBridge(addrstring,store database.Store)*Bridge {
32+
33+
// TODO: remove this.
34+
{
35+
handler:=log.NewWithOptions(os.Stderr, log.Options{
36+
ReportCaller:true,// Enable caller reporting for debuggability
37+
ReportTimestamp:true,
38+
TimeFormat:time.TimeOnly,
39+
})
40+
handler.SetLevel(log.DebugLevel)
41+
slog.SetDefault(slog.New(handler))
42+
}
43+
2144
mux:=&http.ServeMux{}
2245
mux.HandleFunc("/v1/chat/completions",proxyOpenAIRequest)
23-
mux.HandleFunc("/v1/messages",proxyAnthropicRequest)
46+
47+
openAIProvider,err:=openai.NewOpenAIProvider("openai", openai.OpenAIProviderConfig{
48+
BaseURL:"https://api.openai.com/",
49+
Model:"gpt-4o",
50+
})
51+
iferr!=nil {
52+
panic(err)// TODO: don't panic.
53+
}
54+
55+
anthropicProvider,err:=anthropic.NewAnthropicProvider("anthropic", anthropic.AnthropicProviderConfig{
56+
Model:"claude-sonnet-4-0",
57+
BaseURL:"https://api.anthropic.com/",
58+
AnthropicVersion:"2023-06-01",
59+
})
60+
iferr!=nil {
61+
panic(err)// TODO: don't panic.
62+
}
63+
64+
rl,err:=logger.NewRequestLogger(nil)
65+
iferr!=nil {
66+
panic(err)// TODO: don't panic.
67+
}
68+
69+
pipelines:=map[string]server.ModelPipeline{
70+
"gpt-4o": {
71+
Provider:openAIProvider,
72+
},
73+
"claude-sonnet-4-0": {
74+
Provider:anthropicProvider,
75+
RequestChain: []middleware.RequestMiddleware{rl},
76+
//ResponseChain: []middleware.ResponseMiddleware{
77+
//func(req apibridge.Request, origResp apibridge.Response) (apibridge.Response, error) {
78+
//baseID := origResp.ID
79+
//modelName := req.Model
80+
//messages := req.Messages
81+
//
82+
////if resp.StopReason == "stop" {
83+
////event := map[string]any{
84+
////"prompt": req.Messages,
85+
////"usage": resp.Usage,
86+
////}
87+
////eventBytes, err := json.Marshal(event)
88+
////if err != nil {
89+
////return resp, xerrors.Errorf("marshal event: %w", err)
90+
////}
91+
////
92+
////err = store.InsertWormholeEvent(context.TODO(), database.InsertWormholeEventParams{
93+
////Event: eventBytes,
94+
////EventType: "anthropic",
95+
////})
96+
////if err != nil {
97+
////return resp, xerrors.Errorf("wormhole: %w", err)
98+
////}
99+
////}
100+
////
101+
////return resp, nil
102+
//
103+
//resp := apibridge.Response{
104+
//ID: baseID,
105+
//Model: modelName,
106+
//StreamChannel: make(chan apibridge.StreamChunk),
107+
//}
108+
//
109+
//go func() {
110+
//defer close(resp.StreamChannel)
111+
//
112+
//var allParts []string
113+
//for _, message := range messages {
114+
//text := extractMessageText(message.Content)
115+
//if text == "" {
116+
//continue
117+
//}
118+
//rolePrefix := "unknown message: "
119+
//switch message.Role {
120+
//case apibridge.RoleSystem:
121+
//rolePrefix = "system message: "
122+
//case apibridge.RoleUser:
123+
//rolePrefix = "user message: "
124+
//case apibridge.RoleAssistant: // Should not happen in input, but handle
125+
//rolePrefix = "assistant message: "
126+
//}
127+
//allParts = append(allParts, rolePrefix+text)
128+
//}
129+
//
130+
//if len(allParts) == 0 {
131+
//// If no content, send only a final chunk with finish reason
132+
//finalChunk := apibridge.StreamChunk{
133+
//ID: resp.ID,
134+
//Model: resp.Model,
135+
//Choices: []apibridge.StreamChoice{
136+
//{
137+
//Index: 0,
138+
//Delta: apibridge.StreamChoiceDelta{}, // Empty delta
139+
//FinishReason: "stop",
140+
//},
141+
//},
142+
//Usage: &apibridge.Usage{}, // Empty usage
143+
//}
144+
//resp.StreamChannel <- finalChunk
145+
//
146+
//// Send the IsDone chunk for compatibility with server handlers
147+
//// This is needed for the OpenAI handler to send the [DONE] marker
148+
//doneChunk := apibridge.StreamChunk{
149+
//ID: resp.ID,
150+
//Model: resp.Model,
151+
//IsDone: true,
152+
//}
153+
//resp.StreamChannel <- doneChunk
154+
//return
155+
//}
156+
//
157+
//// Send each part as a separate chunk - consistent behavior
158+
//for _, part := range allParts {
159+
//contentChunk := apibridge.StreamChunk{
160+
//ID: resp.ID,
161+
//Model: resp.Model,
162+
//Choices: []apibridge.StreamChoice{
163+
//{
164+
//Index: 0,
165+
//Delta: apibridge.StreamChoiceDelta{
166+
//Role: apibridge.RoleAssistant,
167+
//Content: []apibridge.ContentPart{{Type: apibridge.ContentTypeText, Text: part}},
168+
//},
169+
//},
170+
//},
171+
//}
172+
//resp.StreamChannel <- contentChunk
173+
//}
174+
//
175+
//// Send the final chunk with finish_reason
176+
//finalChunk := apibridge.StreamChunk{
177+
//ID: resp.ID,
178+
//Model: resp.Model,
179+
//Choices: []apibridge.StreamChoice{
180+
//{
181+
//Index: 0,
182+
//Delta: apibridge.StreamChoiceDelta{}, // Empty delta for final chunk
183+
//FinishReason: "stop",
184+
//},
185+
//},
186+
//Usage: &apibridge.Usage{}, // Empty usage
187+
//}
188+
//resp.StreamChannel <- finalChunk
189+
//
190+
//// Send the IsDone chunk for compatibility with server handlers
191+
//// This is needed for the OpenAI handler to send the [DONE] marker
192+
//doneChunk := apibridge.StreamChunk{
193+
//ID: resp.ID,
194+
//Model: resp.Model,
195+
//IsDone: true,
196+
//}
197+
//resp.StreamChannel <- doneChunk
198+
//}()
199+
//
200+
//return resp, nil
201+
//},
202+
//},
203+
},
204+
}
205+
206+
mux.HandleFunc("/v1/messages",server.RouteAnthropicMessages(pipelines))
24207

25208
srv:=&http.Server{
26209
Addr:addr,
@@ -31,6 +214,16 @@ func NewBridge(addr string) *Bridge {
31214
return&Bridge{httpSrv:srv}
32215
}
33216

217+
funcextractMessageText(content []apibridge.ContentPart)string {
218+
varparts []string
219+
for_,cp:=rangecontent {
220+
ifcp.Type==apibridge.ContentTypeText&&cp.Text!="" {
221+
parts=append(parts,cp.Text)
222+
}
223+
}
224+
returnstrings.Join(parts," ")
225+
}
226+
34227
funcproxyOpenAIRequest(w http.ResponseWriter,r*http.Request) {
35228
target,err:=url.Parse("https://api.openai.com")
36229
iferr!=nil {
@@ -89,6 +282,11 @@ func proxyAnthropicRequest(w http.ResponseWriter, r *http.Request) {
89282

90283
fmt.Printf("Proxying %s request to: %s\n",req.Method,req.URL.String())
91284
}
285+
proxy.ModifyResponse=func(response*http.Response)error {
286+
fmt.Println("response",response.ContentLength,response.Status)
287+
288+
returnnil
289+
}
92290
proxy.ServeHTTP(w,r)
93291
}
94292

‎cli/server.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1596,7 +1596,7 @@ func newProvisionerDaemon(
15961596
funcnewAIBridgeDaemon(ctx context.Context,coderAPI*coderd.API,namestring) (*aibridged.Server,error) {
15971597
httpAddr:="0.0.0.0:0"// TODO: configurable.
15981598

1599-
returnaibridged.New(func(dialCtx context.Context) (aibridgedproto.DRPCAIBridgeDaemonClient,error) {
1599+
returnaibridged.New(coderAPI.Database,func(dialCtx context.Context) (aibridgedproto.DRPCAIBridgeDaemonClient,error) {
16001600
// This debounces calls to listen every second.
16011601
// TODO: is this true / necessary?
16021602
returncoderAPI.CreateInMemoryAIBridgeDaemon(dialCtx,name)

‎coderd/database/dbauthz/dbauthz.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3957,6 +3957,10 @@ func (q *querier) InsertWorkspaceResourceMetadata(ctx context.Context, arg datab
39573957
returnq.db.InsertWorkspaceResourceMetadata(ctx,arg)
39583958
}
39593959

3960+
func (q*querier)InsertWormholeEvent(ctx context.Context,arg database.InsertWormholeEventParams)error {
3961+
returnq.db.InsertWormholeEvent(ctx,arg)// TODO: authz.
3962+
}
3963+
39603964
func (q*querier)ListProvisionerKeysByOrganization(ctx context.Context,organizationID uuid.UUID) ([]database.ProvisionerKey,error) {
39613965
returnfetchWithPostFilter(q.auth,policy.ActionRead,q.db.ListProvisionerKeysByOrganization)(ctx,organizationID)
39623966
}

‎coderd/database/dbmem/dbmem.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10178,6 +10178,15 @@ func (q *FakeQuerier) InsertWorkspaceResourceMetadata(_ context.Context, arg dat
1017810178
returnmetadata,nil
1017910179
}
1018010180

10181+
func (q*FakeQuerier)InsertWormholeEvent(ctx context.Context,arg database.InsertWormholeEventParams)error {
10182+
err:=validateDatabaseType(arg)
10183+
iferr!=nil {
10184+
returnerr
10185+
}
10186+
10187+
panic("not implemented")
10188+
}
10189+
1018110190
func (q*FakeQuerier)ListProvisionerKeysByOrganization(_ context.Context,organizationID uuid.UUID) ([]database.ProvisionerKey,error) {
1018210191
q.mutex.RLock()
1018310192
deferq.mutex.RUnlock()

‎coderd/database/dbmetrics/querymetrics.go

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

‎coderd/database/dbmock/dbmock.go

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

‎coderd/database/dump.sql

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
CREATETABLEwormhole
2+
(
3+
id UUID,
4+
created_attimestamptzNOT NULL,
5+
event jsonbNOT NULL,
6+
event_typevarchar(32)NOT NULL
7+
);

‎coderd/database/models.go

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

‎coderd/database/querier.go

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/database/queries.sql.go

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

‎coderd/database/queries/wormhole.sql

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
-- name: InsertWormholeEvent :exec
2+
INSERT INTO wormhole (id, created_at, event, event_type)
3+
VALUES (gen_random_uuid(), now(), @event, @event_type);

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp