Skip to content

Commit 90f930e

Browse files
committed
feat: add dashscope as llm
Signed-off-by: Abirdcfly <[email protected]>
1 parent f9811e7 commit 90f930e

File tree

14 files changed

+686
-20
lines changed

14 files changed

+686
-20
lines changed

README.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ kubectl apply -f config/samples/arcadia_v1alpha1_llm.yaml
2626
kubectl apply -f config/samples/arcadia_v1alpha1_prompt.yaml
2727
```
2828

29-
After prompt got created, you can see the prompt in the following command:
29+
After the prompt got created, you can see the prompt in the following command:
3030

3131
```shell
3232
kubectl get prompt prompt-zhipuai-sample -oyaml
3333
```
3434

35-
If no error found,you can use this command to get the prompt response data.
35+
If no error is found, you can use this command to get the prompt response data.
3636

3737
```shell
3838
kubectl get prompt prompt-zhipuai-sample --output="jsonpath={.status.data}" | base64 --decode
@@ -56,18 +56,18 @@ go install github.com/kubeagi/arcadia/arctl@latest
5656

5757
## Packages
5858

59-
To enhace the AI capability in Golang,we developed some packages.
59+
To enhace the AI capability in Golang, we developed some packages.
6060

6161
### LLMs
6262

63-
-[ZhiPuAI(智谱AI)](https://github.com/kubeagi/arcadia/tree/main/pkg/llms/zhipuai)
63+
-[ZhiPuAI(智谱 AI)](https://github.com/kubeagi/arcadia/tree/main/pkg/llms/zhipuai)
6464
- [example](https://github.com/kubeagi/arcadia/blob/main/examples/zhipuai/main.go)
6565

6666
### Embeddings
6767

6868
> Fully compatible with [langchain embeddings](https://github.com/tmc/langchaingo/tree/main/embeddings)
6969
70-
-[ZhiPuAI(智谱AI) Embedding](https://github.com/kubeagi/arcadia/tree/main/pkg/embeddings/zhipuai)
70+
-[ZhiPuAI(智谱 AI) Embedding](https://github.com/kubeagi/arcadia/tree/main/pkg/embeddings/zhipuai)
7171

7272
### VectorStores
7373

@@ -77,10 +77,10 @@ To enhace the AI capability in Golang,we developed some packages.
7777

7878
## Examples
7979

80-
- [chat_with_document](https://github.com/kubeagi/arcadia/tree/main/examples/chat_with_document): a chat server which allows you chat with your document
80+
- [chat_with_document](https://github.com/kubeagi/arcadia/tree/main/examples/chat_with_document): a chat server which allows you to chat with your document
8181
- [embedding](https://github.com/kubeagi/arcadia/tree/main/examples/embedding) shows how to embedes your document to vector store with embedding service
82-
- [rbac](https://github.com/kubeagi/arcadia/blob/main/examples/rbac/main.go) shows to to inquiry the security risks in your RBAC with AI.
83-
- [zhipuai](https://github.com/kubeagi/arcadia/blob/main/examples/zhipuai/main.go) show how to use this [zhipuai client](https://github.com/kubeagi/arcadia/tree/main/pkg/llms/zhipuai)
82+
- [rbac](https://github.com/kubeagi/arcadia/blob/main/examples/rbac/main.go) shows how to inquiry the security risks in your RBAC with AI.
83+
- [zhipuai](https://github.com/kubeagi/arcadia/blob/main/examples/zhipuai/main.go) shows how to use this [zhipuai client](https://github.com/kubeagi/arcadia/tree/main/pkg/llms/zhipuai)
8484

8585
## Contribute to Arcadia
8686

controllers/prompt_controller.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ func (r *PromptReconciler) CallLLM(ctx context.Context, logger logr.Logger, prom
100100
switch llm.Spec.Type {
101101
case llms.ZhiPuAI:
102102
llmClient = llmszhipuai.NewZhiPuAI(apiKey)
103-
callData = prompt.Spec.ZhiPuAIParams.Marshall()
103+
callData = prompt.Spec.ZhiPuAIParams.Marshal()
104104
case llms.OpenAI:
105105
llmClient = openai.NewOpenAI(apiKey)
106106
default:

examples/dashscope/main.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
Copyright 2023 KubeAGI.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package main
18+
19+
import (
20+
"context"
21+
"os"
22+
23+
"github.com/kubeagi/arcadia/pkg/llms"
24+
"github.com/kubeagi/arcadia/pkg/llms/dashscope"
25+
"k8s.io/klog/v2"
26+
)
27+
28+
const (
29+
samplePrompt = "how to change a deployment's image?"
30+
)
31+
32+
func main() {
33+
if len(os.Args) == 1 {
34+
panic("api key is empty")
35+
}
36+
apiKey := os.Args[1]
37+
klog.Infof("sample chat start...\nwe use same prompt: %s to test\n", samplePrompt)
38+
for _, model := range []dashscope.Model{dashscope.QWEN14BChat, dashscope.QWEN7BChat} {
39+
klog.V(0).Infof("\nChat with %s\n", model)
40+
resp, err := sampleChat(apiKey, model)
41+
if err != nil {
42+
panic(err)
43+
}
44+
klog.V(0).Infof("Response: \n %s\n", resp)
45+
klog.V(0).Infoln("\nChat again with sse enable")
46+
err = sampleSSEChat(apiKey, model)
47+
if err != nil {
48+
panic(err)
49+
}
50+
}
51+
klog.Infoln("sample chat done")
52+
}
53+
54+
func sampleChat(apiKey string, model dashscope.Model) (llms.Response, error) {
55+
client := dashscope.NewDashScope(apiKey, false)
56+
params := dashscope.DefaultModelParams()
57+
params.Model = model
58+
params.Input.Messages = []dashscope.Message{
59+
{Role: dashscope.System, Content: "You are a kubernetes expert."},
60+
{Role: dashscope.User, Content: samplePrompt},
61+
}
62+
return client.Call(params.Marshal())
63+
}
64+
65+
func sampleSSEChat(apiKey string, model dashscope.Model) error {
66+
client := dashscope.NewDashScope(apiKey, true)
67+
params := dashscope.DefaultModelParams()
68+
params.Model = model
69+
params.Input.Messages = []dashscope.Message{
70+
{Role: dashscope.System, Content: "You are a kubernetes expert."},
71+
{Role: dashscope.User, Content: samplePrompt},
72+
}
73+
// you can define a customized `handler` on `Event`
74+
err := client.StreamCall(context.TODO(), params.Marshal(), nil)
75+
if err != nil {
76+
return err
77+
}
78+
return nil
79+
}

pkg/llms/dashscope/api.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
Copyright 2023 KubeAGI.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package dashscope
18+
19+
import (
20+
"context"
21+
"errors"
22+
23+
"github.com/kubeagi/arcadia/pkg/llms"
24+
)
25+
26+
const (
27+
DashScopeChatURL = "https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation"
28+
)
29+
30+
type Model string
31+
32+
const (
33+
QWEN14BChat Model = "qwen-14b-chat"
34+
QWEN7BChat Model = "qwen-7b-chat"
35+
)
36+
37+
var _ llms.LLM = (*DashScope)(nil)
38+
39+
type DashScope struct {
40+
apiKey string
41+
sse bool
42+
}
43+
44+
func NewDashScope(apiKey string, sse bool) *DashScope {
45+
return &DashScope{
46+
apiKey: apiKey,
47+
sse: sse,
48+
}
49+
}
50+
51+
func (z DashScope) Type() llms.LLMType {
52+
return llms.DashScope
53+
}
54+
55+
// Call wraps a common AI api call
56+
func (z *DashScope) Call(data []byte) (llms.Response, error) {
57+
params := ModelParams{}
58+
if err := params.Unmarshal(data); err != nil {
59+
return nil, err
60+
}
61+
return do(context.TODO(), DashScopeChatURL, z.apiKey, data, z.sse)
62+
}
63+
64+
func (z *DashScope) Validate() (llms.Response, error) {
65+
return nil, errors.New("not implemented")
66+
}

pkg/llms/dashscope/http_client.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
Copyright 2023 KubeAGI.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package dashscope
18+
19+
import (
20+
"bytes"
21+
"context"
22+
"encoding/json"
23+
"net/http"
24+
)
25+
26+
func setHeaders(req *http.Request, token string, sse bool) {
27+
if sse {
28+
// req.Header.Set("Content-Type", "text/event-stream") // Although the documentation says we should do this, but will return a 400 error and the python sdk doesn't do this.
29+
req.Header.Set("Content-Type", "application/json")
30+
req.Header.Set("Accept", "text/event-stream")
31+
req.Header.Set("X-DashScope-SSE", "enable")
32+
} else {
33+
req.Header.Set("Content-Type", "application/json")
34+
req.Header.Set("Accept", "*/*")
35+
}
36+
req.Header.Set("Authorization", "Bearer "+token)
37+
}
38+
39+
func parseHTTPResponse(resp *http.Response) (data *Response, err error) {
40+
if err = json.NewDecoder(resp.Body).Decode(&data); err != nil {
41+
return nil, err
42+
}
43+
return data, nil
44+
}
45+
46+
func req(ctx context.Context, apiURL, token string, data []byte, sse bool) (*http.Response, error) {
47+
req, err := http.NewRequestWithContext(ctx, "POST", apiURL, bytes.NewReader(data))
48+
if err != nil {
49+
return nil, err
50+
}
51+
52+
setHeaders(req, token, sse)
53+
54+
return http.DefaultClient.Do(req)
55+
}
56+
func do(ctx context.Context, apiURL, token string, data []byte, sse bool) (*Response, error) {
57+
resp, err := req(ctx, apiURL, token, data, sse)
58+
if err != nil {
59+
return nil, err
60+
}
61+
defer resp.Body.Close()
62+
return parseHTTPResponse(resp)
63+
}

pkg/llms/dashscope/params.go

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/*
2+
Copyright 2023 KubeAGI.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
// NOTE: Reference zhipuai's python sdk: model_api/params.py
18+
19+
package dashscope
20+
21+
import (
22+
"encoding/json"
23+
"errors"
24+
25+
"github.com/kubeagi/arcadia/pkg/llms"
26+
)
27+
28+
type Role string
29+
30+
const (
31+
System Role = "system"
32+
User Role = "user"
33+
Assistant Role = "assistant"
34+
)
35+
36+
var _ llms.ModelParams = (*ModelParams)(nil)
37+
38+
// +kubebuilder:object:generate=true
39+
40+
// ModelParams
41+
// ref: https://help.aliyun.com/zh/dashscope/developer-reference/tongyi-qianwen-7b-14b-api-detailes#25745d61fbx49
42+
// do not use 'input.history', according to the above document, this parameter will be deprecated soon.
43+
// use 'message' in 'parameters.result_format' to keep better compatibility.
44+
type ModelParams struct {
45+
Model Model `json:"model"`
46+
Input Input `json:"input"`
47+
Parameters Parameters `json:"parameters"`
48+
}
49+
50+
// +kubebuilder:object:generate=true
51+
52+
type Input struct {
53+
Messages []Message `json:"messages"`
54+
}
55+
56+
type Parameters struct {
57+
TopP float32 `json:"top_p,omitempty"`
58+
TopK int `json:"top_k,omitempty"`
59+
Seed int `json:"seed,omitempty"`
60+
ResultFormat string `json:"result_format,omitempty"`
61+
}
62+
63+
// +kubebuilder:object:generate=true
64+
65+
type Message struct {
66+
Role Role `json:"role,omitempty"`
67+
Content string `json:"content,omitempty"`
68+
}
69+
70+
func DefaultModelParams() ModelParams {
71+
return ModelParams{
72+
Model: QWEN14BChat,
73+
Input: Input{
74+
Messages: []Message{},
75+
},
76+
Parameters: Parameters{
77+
TopP: 0.5,
78+
TopK: 0,
79+
Seed: 1234,
80+
ResultFormat: "message",
81+
},
82+
}
83+
}
84+
85+
func (params *ModelParams) Marshal() []byte {
86+
data, err := json.Marshal(params)
87+
if err != nil {
88+
return []byte{}
89+
}
90+
return data
91+
}
92+
93+
func (params *ModelParams) Unmarshal(bytes []byte) error {
94+
return json.Unmarshal(bytes, params)
95+
}
96+
97+
func ValidateModelParams(params ModelParams) error {
98+
if params.Parameters.TopP < 0 || params.Parameters.TopP > 1 {
99+
return errors.New("top_p must be in (0, 1)")
100+
}
101+
102+
return nil
103+
}

0 commit comments

Comments
 (0)