Skip to content

Commit 001235e

Browse files
xxx7xxxxsuchen-sciLokiWager
authored
Integrate AI Gateway feature into main (#1461)
* add basic framework for ai gateway * update content length for fix length payload * update interfaces * Add Observability option placeholder & Validate * Correct arguments and Add registry of AIController * proxy request to openai providers * [enhancement] refactor error define consist with openai api reference and handle error before using response * [enhancement] abstract the compatible openai logic to base provider * support parse openai resp to get input and output tokens * rm unnecessary goroutine * Register and Unregister APIs * [enhancement] add multiple providers * [enhancement] add Mistral provider * [enhancement] adjust for auzre api * add prometheus metrics * [enhancement] more support api * refactor ProviderContext to support callbacks and avoid re-unmarshal during middleware processing * fix typo * handle err case for proxy request * [enhancement] add embedding & image protocol * [enhancement] support embedding & image generations usage * Implement health check for providers * Correct health check path * support in-memory metric stats for ai-gateway-controller * update layout based on comments * align AIGateway in-memory metrics with prometheus metrics, refacotr get metric stats process * Implement Stats API * Delete redundant line for object API * refactor aigateway context to simplify provider impl and support future middlewares * [enhancement] registry providers * Add AIGatewayController config * Fix providers registry types * fix prometheus missing labels * [enhancement] set stream options to true default * [refine] refactor metrics send logic * register ai-gateway apis when inherit * fix inherit bugs, reuse metrichub for in-memory metrics and prometheus metrics consistency * Move registerAPIs to lifecycle-insensitive reload * Add config of AIGateway pipeline * Add registry of AIGatewayProxy * Set omitempty for middlewares * update provider spec validate process * add semantic cache middlware framework for embedding and vectordb * [refine] refactor vector db framework * fix import errors, optim code structure * add ollama embedding * add openai embedding * Add HTTPServer config and curl example for AI Gateway * update vectordb interface * add semanticcache middleware handle logics * add create middleware logics * Add APIKey in health check * Add DeepSeek Provider config * fix proxy models api error * [enhancement] support redis index create * [enhancement] support redis index add(batch) & query doc * add benchmark scripts for mock llm and eg * Run go mod tidy * Add egctl ai command * [enhancement] add redis implement for vectordb * update semantic cache logics based on redis vector db * fix type conversion panics * fix parse invalid openai request * fix benchmark mock llm * Implement egctl ai stat/status and support table format in stat * add apisix benchmark scripts * add kong benchmark scripts * Support human-readable output for egctl ai check * Add successful message for egctl ai enable/disable * fix benchmark linux network problems * Support egctl ai edit * [testing] unit tests for redis and bug fix * [testing] add integration tests with testContainers & fix bug * [testing] typo * [enhancement] add framework for postgres * fix build error * add tests for AIGatewayProxy * fix create ai context * add test for AIGatewayController * Fix the match of register and unregister of APIs (#1448) * Docs: Add tutorial of AI Gateway * Docs: Fix some problems from markdown lint * [enhancement] add insert & query logic for pg * Docs: Add reference for AIGatewayController * Docs: Fix table format for AccessLogVariable * add test for AIContext * add test for MetricsHub * add test for middleware embeddings * add test for SemanticCache * [testing] fix bugs for vector type * [testing] refactor to make it more testable * [testing] add unit test for concatenate SQL * [testing] add integration test for pg vector * [testing] refactor import * add test for providers * fix typo * Docs: Add filter AIGatewayProxy * Docs: Add jump links for AIGatewayProxy * [testing] add pg spec * Add json tags for redis and postgres in vectordb * Docs: Add postgres for vectordb * add pg backend for semanticcache * fix semanticcache test * add benchmark result and doc * Docs: Add AI feature in README * fix test race condition * add copyright for missing files * Add license for api.go * fix ci lint * fix OpenTelemetry schema version error * skip docker tests for github action mac and windows runner --------- Co-authored-by: chen <[email protected]> Co-authored-by: LokiWager <[email protected]>
1 parent 54c1e6d commit 001235e

File tree

94 files changed

+9510
-289
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

94 files changed

+9510
-289
lines changed

.github/workflows/test.yml

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,12 @@ env:
2525
GO_VERSION: "1.23"
2626

2727
jobs:
28-
test:
28+
test-ubuntu:
2929
runs-on: ${{ matrix.os }}
3030
strategy:
3131
fail-fast: false
3232
matrix:
33-
os: [ubuntu-latest, macos-latest]
33+
os: [ubuntu-latest]
3434
steps:
3535
- name: Set up Go 1.x.y
3636
uses: actions/setup-go@v4
@@ -49,6 +49,28 @@ jobs:
4949
uses: codecov/[email protected]
5050
with:
5151
file: ./coverage.txt
52+
test-macos:
53+
runs-on: ${{ matrix.os }}
54+
strategy:
55+
fail-fast: false
56+
matrix:
57+
os: [macos-latest]
58+
steps:
59+
- name: Set up Go 1.x.y
60+
uses: actions/setup-go@v4
61+
with:
62+
go-version: ${{ env.GO_VERSION }}
63+
64+
- name: Checkout codebase
65+
uses: actions/checkout@v3
66+
67+
- name: Test
68+
shell: bash
69+
env:
70+
EASEGRESS_TEST_SKIP_DOCKER: "true"
71+
run: |
72+
make test TEST_FLAGS="-race -coverprofile=coverage.txt -covermode=atomic"
73+
5274
test-win:
5375
runs-on: windows-latest
5476
strategy:
@@ -63,6 +85,8 @@ jobs:
6385
uses: actions/checkout@v3
6486

6587
- name: Test
88+
env:
89+
EASEGRESS_TEST_SKIP_DOCKER: "true"
6690
run: |
6791
go mod verify
6892
go mod download

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,10 @@ And you can check [Easegress DeepWiki Page](https://deepwiki.com/easegress-io/ea
104104
- **Data Size:** request and response size.
105105
- **Status Codes:** HTTP status codes.
106106
- **TopN:** sorted by aggregated APIs(only in server dimension).
107+
- **AI Integration**
108+
- **Proxy:** proxy requests to LLM providers like OpenAI, DeepSeek, Anthropic, etc.
109+
- **Vector Database:** integrates with vector databases for caching.
110+
- **Monitoring:** provides insights into the performance and usage of AI models.
107111

108112
## Getting Started
109113

README.zh-CN.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@
9797
- **数据大小**:请求和响应大小。
9898
- **状态代码**:HTTP状态代码。
9999
- **TopN**:按 API 聚合并排序(仅服务器维度)。
100+
- **AI 集成**
101+
- **代理:** 支持将请求代理到 LLM 提供商,如 OpenAI、DeepSeek、Anthropic 等。
102+
- **向量数据库:** 集成向量数据库以实现缓存。
103+
- **监控:** 提供对 AI 模型性能和使用情况的监控。
100104

101105
## 用户案例
102106

cmd/client/commandv2/ai.go

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
/*
2+
* Copyright (c) 2017, The Easegress Authors
3+
* All rights reserved.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
// Package commandv2 provides the new version of commands.
19+
package commandv2
20+
21+
import (
22+
"fmt"
23+
"net/http"
24+
25+
"github.com/megaease/easegress/v2/cmd/client/general"
26+
"github.com/megaease/easegress/v2/cmd/client/resources"
27+
"github.com/megaease/easegress/v2/pkg/object/aigatewaycontroller"
28+
"github.com/megaease/easegress/v2/pkg/object/aigatewaycontroller/metricshub"
29+
"github.com/megaease/easegress/v2/pkg/util/codectool"
30+
"github.com/spf13/cobra"
31+
)
32+
33+
// AICmd returns AI command.
34+
func AICmd() *cobra.Command {
35+
examples := []general.Example{
36+
{Desc: "Enable AI (Create the AIGatewayController)", Command: "egctl enable"},
37+
{Desc: "Disable AI (Delete the AIGatewayController)", Command: "egctl disable"},
38+
{Desc: "Get AI statistics", Command: "egctl ai stat"},
39+
{Desc: "Check AI health of providers", Command: "egctl ai check"},
40+
}
41+
42+
cmd := &cobra.Command{
43+
Use: "ai",
44+
Short: "Commands to manage AI Gateway",
45+
Args: cobra.NoArgs,
46+
Example: createMultiExample(examples),
47+
Run: aiCmdRun,
48+
}
49+
50+
cmd.AddCommand(
51+
enableCmd(),
52+
disableCmd(),
53+
statCmd(),
54+
checkCmd(),
55+
editCmd(),
56+
)
57+
58+
return cmd
59+
}
60+
61+
func aiCmdRun(cmd *cobra.Command, args []string) {
62+
cmd.Help()
63+
}
64+
65+
func enableCmd() *cobra.Command {
66+
return &cobra.Command{
67+
Use: "enable",
68+
Short: "Enable AI Gateway (create AIGatewayController)",
69+
Run: func(cmd *cobra.Command, args []string) {
70+
var err error
71+
defer func() {
72+
if err != nil {
73+
general.ExitWithError(err)
74+
}
75+
76+
fmt.Println("AI Gateway enabled successfully.")
77+
fmt.Println("You can use `egctl ai edit` to add providers and middlewares.")
78+
}()
79+
80+
s, err := general.GetSpecFromYaml(`kind: AIGatewayController
81+
name: AIGatewayController
82+
`)
83+
if err != nil {
84+
return
85+
}
86+
87+
err = resources.CreateObject(cmd, s)
88+
},
89+
}
90+
}
91+
92+
func disableCmd() *cobra.Command {
93+
return &cobra.Command{
94+
Use: "disable",
95+
Short: "Disable AI Gateway (delete AIGatewayController)",
96+
Run: func(cmd *cobra.Command, args []string) {
97+
var err error
98+
defer func() {
99+
if err != nil {
100+
general.ExitWithError(err)
101+
}
102+
fmt.Println("AI Gateway disabled successfully.")
103+
}()
104+
105+
err = resources.DeleteObject(cmd, "AIGatewayController", []string{"AIGatewayController"}, false)
106+
},
107+
}
108+
}
109+
110+
func statCmd() *cobra.Command {
111+
return &cobra.Command{
112+
Use: "stat",
113+
Short: "Get AI Gateway statistics",
114+
Example: createExample("Get AI Gateway statistics.", "egctl ai stat"),
115+
Args: cobra.NoArgs,
116+
Run: func(cmd *cobra.Command, args []string) {
117+
body, err := general.HandleRequest(http.MethodGet, general.AIStatURL, nil)
118+
if err != nil {
119+
general.ExitWithError(err)
120+
}
121+
122+
if !general.CmdGlobalFlags.DefaultFormat() {
123+
general.PrintBody(body)
124+
return
125+
}
126+
127+
type AIStatResponse struct {
128+
Stats []metricshub.MetricStats `json:"stats"`
129+
}
130+
131+
var statResp AIStatResponse
132+
err = codectool.UnmarshalJSON(body, &statResp)
133+
if err != nil {
134+
general.ExitWithError(err)
135+
}
136+
137+
// Output table:
138+
// PROVIDER (TYPE), MODEL @ BASEURL, RESP_TYPE,
139+
// TOTAL_REQUESTS, SUCCESS/FAILED, AVG_DURATION(ms),
140+
// TOKENS (INPUT/OUTPUT)
141+
142+
table := [][]string{
143+
{
144+
"PROVIDER(TYPE)",
145+
"MODEL@BASEURL",
146+
"RESP-TYPE",
147+
"TOTAL-REQ",
148+
"SUCCESS/FAILED",
149+
"AVG-DUR(ms)",
150+
"TOKENS(INPUT/OUTPUT)",
151+
},
152+
}
153+
for _, stat := range statResp.Stats {
154+
table = append(table, []string{
155+
fmt.Sprintf("%s(%s)", stat.Provider, stat.ProviderType),
156+
fmt.Sprintf("%s@%s", stat.Model, stat.BaseURL),
157+
stat.RespType,
158+
fmt.Sprintf("%d", stat.TotalRequests),
159+
fmt.Sprintf("%d/%d", stat.SuccessRequests, stat.FailedRequests),
160+
fmt.Sprintf("%d", stat.RequestAverageDuration),
161+
fmt.Sprintf("%d/%d", stat.PromptTokens, stat.CompletionTokens),
162+
})
163+
}
164+
general.PrintTable(table)
165+
},
166+
}
167+
}
168+
169+
func checkCmd() *cobra.Command {
170+
return &cobra.Command{
171+
Use: "check",
172+
Short: "Check AI Gateway health of providers",
173+
Args: cobra.NoArgs,
174+
Run: func(cmd *cobra.Command, args []string) {
175+
body, err := general.HandleRequest(http.MethodGet, general.AISProviderstatusURL, nil)
176+
if err != nil {
177+
general.ExitWithError(err)
178+
}
179+
if !general.CmdGlobalFlags.DefaultFormat() {
180+
general.PrintBody(body)
181+
return
182+
}
183+
184+
var statusResp aigatewaycontroller.HealthCheckResponse
185+
err = codectool.UnmarshalJSON(body, &statusResp)
186+
if err != nil {
187+
general.ExitWithError(err)
188+
}
189+
190+
table := [][]string{
191+
{"NAME", "PROVIDER-TYPE", "HEALTHY"},
192+
}
193+
for _, result := range statusResp.Results {
194+
var healthy string
195+
if result.Healthy {
196+
healthy = "YES"
197+
} else {
198+
healthy = fmt.Sprintf("NO(%s)", result.Error)
199+
}
200+
201+
table = append(table, []string{
202+
result.Name,
203+
result.ProviderType,
204+
healthy,
205+
})
206+
}
207+
general.PrintTable(table)
208+
},
209+
}
210+
}
211+
212+
func editCmd() *cobra.Command {
213+
return &cobra.Command{
214+
Use: "edit",
215+
Short: "Edit AI Gateway providers",
216+
Run: func(cmd *cobra.Command, args []string) {
217+
args = []string{"AIGatewayController", "AIGatewayController"}
218+
editCmdRun(cmd, args)
219+
},
220+
}
221+
}

cmd/client/general/urls.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ const (
7575
// MetricsURL is the URL of metrics.
7676
MetricsURL = APIURL + "/metrics"
7777

78+
AISProviderstatusURL = APIURL + "/ai-gateway/providers/status"
79+
AIStatURL = APIURL + "/ai-gateway/stat"
80+
7881
// HTTPProtocol is prefix for HTTP protocol
7982
HTTPProtocol = "http://"
8083
// HTTPSProtocol is prefix for HTTPS protocol

cmd/client/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ func main() {
9898
commandv2.ConfigCmd(),
9999
commandv2.LogsCmd(),
100100
commandv2.MetricsCmd(),
101+
commandv2.AICmd(),
101102
)
102103

103104
addCommandWithGroup(

cmd/client/resources/resources.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ func editErrWithPath(err error, filePath string) error {
7676

7777
func editResource(oldYaml, filePath string) (string, error) {
7878
var err error
79-
if err = os.WriteFile(filePath, []byte(oldYaml), 0644); err != nil {
79+
if err = os.WriteFile(filePath, []byte(oldYaml), 0o644); err != nil {
8080
return "", err
8181
}
8282
// exec editor and get new yaml

0 commit comments

Comments
 (0)