Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
85 changes: 85 additions & 0 deletions grpcServer/anthropic.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package grpcServer

import (
"go.limit.dev/unollm/model"
anthropic "go.limit.dev/unollm/provider/Anthropic"
)

func NewAnthropicClient(info *model.LLMRequestInfo) *anthropic.Client {
return anthropic.NewClient(info.GetToken())
}

func AnthropicChatCompletion(cli *anthropic.Client, rs *model.LLMRequestSchema) (*model.LLMResponseSchema, error) {
req := anthropic.ChatCompletionRequest{
Model: rs.LlmRequestInfo.Model,
Messages: make([]anthropic.Message, 0),
MaxTokens: 4096,
Temperature: rs.LlmRequestInfo.Temperature,
TopP: rs.LlmRequestInfo.TopP,
TopK: rs.LlmRequestInfo.TopK,
}
for _, m := range rs.Messages {
req.Messages = append(req.Messages, anthropic.Message{
Role: m.Role,
Content: m.Content,
})
}
respp, err := anthropic.ChatCompletion(cli, req)
if err != nil {
return nil, err
}
res := &model.LLMResponseSchema{
Message: &model.LLMChatCompletionMessage{
Role: respp.Role,
Content: respp.Content[0].Text,
},
LlmTokenCount: &model.LLMTokenCount{
PromptToken: int64(respp.Usage.InputTokens),
CompletionToken: int64(respp.Usage.OutputTokens),
TotalToken: int64(respp.Usage.InputTokens + respp.Usage.OutputTokens),
},
}

return res, nil
}

func AnthropicChatCompletionStreaming(cli *anthropic.Client, rs *model.LLMRequestSchema, sv model.UnoLLMv1_StreamRequestLLMServer) error {
req := anthropic.ChatCompletionRequest{
Model: rs.LlmRequestInfo.Model,
Messages: make([]anthropic.Message, 0),
MaxTokens: 4096,
}
for _, m := range rs.Messages {
req.Messages = append(req.Messages, anthropic.Message{
Role: m.Role,
Content: m.Content,
})
}
respp, err := cli.ChatCompletionStreamingRequest(&req)
if err != nil {
return err
}
go func() {
for {
r := <-respp
if r.Type == "STOP" {
sv.Send(&model.PartialLLMResponse{
Response: &model.PartialLLMResponse_Done{},
LlmTokenCount: &model.LLMTokenCount{
PromptToken: int64(r.Delta.Usage.InputTokens),
CompletionToken: int64(r.Delta.Usage.OutputTokens),
TotalToken: int64(r.Delta.Usage.InputTokens + r.Delta.Usage.OutputTokens),
},
})
return
} else {
sv.Send(&model.PartialLLMResponse{
Response: &model.PartialLLMResponse_Content{
Content: r.Delta.Text,
},
})
}
}
}()
return nil
}
3 changes: 2 additions & 1 deletion grpcServer/baichuan.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
package grpcServer

import (
"log"

"go.limit.dev/unollm/model"
"go.limit.dev/unollm/provider/Baichuan"
"go.limit.dev/unollm/relay"
"go.limit.dev/unollm/relay/reqTransformer"
"go.limit.dev/unollm/relay/respTransformer"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"log"
)

func BaichuanChatCompletion(cli *Baichuan.Client, rs *model.LLMRequestSchema) (*model.LLMResponseSchema, error) {
Expand Down
8 changes: 7 additions & 1 deletion grpcServer/relay.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,17 @@ const AZURE_OPENAI_LLM_API = "azure_openai"
const BAICHUAN_LLM_API = "baichuan"
const GEMINI_LLM_API = "gemini"
const MOONSHOT_LLM_API = "moonshot"
const ANTHROPIC_LLM_API = "anthropic"

func (uno *UnoForwardServer) BlockingRequestLLM(ctx context.Context, rs *model.LLMRequestSchema) (*model.LLMResponseSchema, error) {
info := rs.GetLlmRequestInfo()
switch info.GetLlmApiType() {
case OPENAI_LLM_API:
cli := NewOpenAIClient(info)
return OpenAIChatCompletion(cli, rs)

case ANTHROPIC_LLM_API:
cli := NewAnthropicClient(info)
return AnthropicChatCompletion(cli, rs)
case MOONSHOT_LLM_API:
cli := NewOpenAIClient(info)
if functionCallingRequestMake(rs) {
Expand Down Expand Up @@ -82,6 +85,9 @@ func (uno *UnoForwardServer) StreamRequestLLM(rs *model.LLMRequestSchema, sv mod
case OPENAI_LLM_API:
cli := NewOpenAIClient(info)
return OpenAIChatCompletionStreaming(cli, rs, sv)
case ANTHROPIC_LLM_API:
cli := NewAnthropicClient(info)
return AnthropicChatCompletionStreaming(cli, rs, sv)
case MOONSHOT_LLM_API:
cli := NewOpenAIClient(info)
if functionCallingRequestMake(rs) {
Expand Down
281 changes: 145 additions & 136 deletions model/unollm.pb.go

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions model/unollm.proto
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ message LLMChatCompletionMessage {
string role = 1;
string content = 2;
optional string tool_call_id = 3;
repeated string images = 4;
}


Expand Down
87 changes: 87 additions & 0 deletions provider/Anthropic/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package anthropic

import (
"encoding/xml"
"net/http"
)

type Client struct {
key string
hc *http.Client
}

func NewClient(key string) *Client {
return &Client{
key: key,
hc: &http.Client{},
}
}

type Message struct {
Role string `json:"role"`
Content string `json:"content"`
}

type ChatCompletionRequest struct {
Model string `json:"model"`
Messages []Message `json:"messages"`
MaxTokens int `json:"max_tokens"` // 4096
Stream bool `json:"stream,omitempty"`
Temperature float64 `json:"temperature,omitempty"`
TopP float64 `json:"top_p,omitempty"`
TopK float64 `json:"top_k,omitempty"`
}

type Content struct {
Text string `json:"text"`
Type string `json:"type"`
}

type Usage struct {
InputTokens int `json:"input_tokens"`
OutputTokens int `json:"output_tokens"`
}
type ChatCompletionResponse struct {
Content []Content `json:"content"`
Id string `json:"id"`
Model string `json:"model"`
Role string `json:"role"`
StopReason string `json:"stop_reason"`
StopSequence string `json:"stop_sequence,omitempty"`
Type string `json:"type"`
Usage Usage `json:"usage"`
}

const OPUS = "claude-3-opus-20240229"
const SONNET = "claude-3-sonnet-20240229"

type FunctionCalling struct {
XMLName xml.Name `xml:"config"` // 指定最外层的标签为config
}

// type StreamMessageStartMessage struct {
// Id string `json:"id"`
// Type string `json:"type"`
// Role string `json:"role"`
// Model string `json:"model"`
// Usage Usage `json:"usage"`
// }

// type StreamMessageStart struct {
// Type string `json:"type"`
// Message StreamMessageStartMessage `json:"message"`
// }

type StreamResponseDelta struct {
Type string `json:"type"`
Text string `json:"text"`
StopReason string `json:"stop_reason"`
StopSequence string `json:"stop_sequence"`
Usage Usage `json:"usage"`
}

type StreamResponse struct {
Type string `json:"type"`
Index int `json:"index"`
Delta StreamResponseDelta `json:"delta"`
}
42 changes: 42 additions & 0 deletions provider/Anthropic/chat_completion.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package anthropic

import (
"bytes"
"encoding/json"
"io"
"net/http"
)

func ChatCompletion(cli *Client, req ChatCompletionRequest) (*ChatCompletionResponse, error) {
reqBytes, err := json.Marshal(req)
if err != nil {
return nil, err
}

httpreq, err := http.NewRequest(
"POST",
"https://api.anthropic.com/v1/messages",
bytes.NewBuffer(reqBytes),
)
if err != nil {
return nil, err
}
httpreq.Header.Set("x-api-key", cli.key)
httpreq.Header.Set("anthropic-version", "2023-06-01")
httpreq.Header.Set("content-type", "application/json")
resp, err := cli.hc.Do(httpreq)
if err != nil {
return nil, err
}
defer resp.Body.Close()
bts, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var respp ChatCompletionResponse
err = json.Unmarshal(bts, &respp)
if err != nil {
return nil, err
}
return &respp, nil
}
79 changes: 79 additions & 0 deletions provider/Anthropic/stream_chat_completion.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package anthropic

import (
"bytes"
"encoding/json"
"log"
"net/http"
"strings"

"go.limit.dev/unollm/utils"
)

func (c *Client) ChatCompletionStreamingRequest(body *ChatCompletionRequest) (chan StreamResponse, error) {
body.Stream = true
reqBody, err := json.Marshal(body)
if err != nil {
return nil, err

}

req, err := http.NewRequest("POST", "https://api.anthropic.com/v1/messages", bytes.NewReader(reqBody))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("x-api-key", c.key)
req.Header.Set("anthropic-version", "2023-06-01")
req.Header.Set("X-Stainless-Stream-Helper", "messages")

resp, err := c.hc.Do(req)
if err != nil {
return nil, err

}

reader := utils.NewEventStreamReader(resp.Body, 4096)

res := make(chan StreamResponse)

go func() error {
defer resp.Body.Close()
var jj map[string]any
var jjj StreamResponse
for reader.Scanner.Scan() {
text := reader.Scanner.Text()
// read from first \n to the end of the text
event := text[:strings.Index(text, "\n")]
text = text[strings.Index(text, "\n")+1+6:]
log.Println(event)
log.Println(text)
if strings.Contains(event, "error") {
log.Println(text)
break
}
if strings.Contains(event, "message_stop") {
jjj.Delta.Usage.InputTokens = int(jj["message"].(map[string]any)["usage"].(map[string]any)["input_tokens"].(float64))
res <- StreamResponse{Type: "STOP", Delta: StreamResponseDelta{
Usage: jjj.Delta.Usage,
}}
return nil
}
if jj != nil {
err = json.NewDecoder(strings.NewReader(text)).Decode(&jjj)
if err != nil {
log.Println(err)
return nil
}
res <- jjj
} else {
err := json.NewDecoder(strings.NewReader(text)).Decode(&jj)
if err != nil {
break
}
}
}
return reader.Scanner.Err()
}()
return res, nil
}
15 changes: 14 additions & 1 deletion provider/ChatGLM/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,22 @@ const (
ModelGLM4V = "glm-4v"
)

type ChatCompletionTextContent struct {
Type string `json:"type"`
Text string `json:"text"`
}

type Image struct {
Url string `json:"url"`
}
type ChatCompletionImageContent struct {
Type string `json:"type"`
ImageUrl Image `json:"image_url"`
}

type ChatCompletionMessage struct {
Role string `json:"role"`
Content string `json:"content"`
Content any `json:"content"`
ToolCalls []GLMToolCall `json:"tool_calls,omitempty"`
}

Expand Down
Loading