diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index c8806d68..ca963b82 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -56,7 +56,7 @@ body: label: Environment description: Share your environment details. Reports without proper environment details will likely be closed. value: | - - APINTO Dashboard version: + - ApiPark version: - Operating system (run `uname -a`): validations: required: true \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index 035b15b9..00000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,99 +0,0 @@ -variables: - PATH: /opt/go-1.23/go/bin/:/opt/node-1.22/bin/:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin - GOROOT: /opt/go-1.23/go - GOPROXY: https://goproxy.cn - VERSION: $CI_COMMIT_SHORT_SHA - APP: apipark - APP_PRE: ${APP}_${VERSION} - BUILD_DIR: ${APP}-build - DEPLOY_DESC: "DEV 环境" - VIEW_ADDR: http://172.18.166.219:8288 - SAVE_DIR: /opt/${APP} - NODE_OPTIONS: --max_old_space_size=8192 - APIPARK_OLLAMA_BASE: http://127.0.0.1:11434 - -stages: - - notice - - prefix - - build - - deploy - - webhook - -feishu-informer: # 飞书回调 - stage: notice - variables: - DIFF_URL: "$CI_MERGE_REQUEST_PROJECT_URL/-/merge_requests/$CI_MERGE_REQUEST_IID/diffs" - rules: - - if: $CI_PIPELINE_SOURCE=="merge_request_event" && $CI_COMMIT_BRANCH =~ "main" - script: - - echo "merge request" - - | - curl -X POST -H "Content-Type: application/json" \ - -d "{\"msg_type\":\"text\",\"content\":{\"text\":\"项目:${CI_PROJECT_NAME}\\n提交人:${GITLAB_USER_NAME}\\n提交信息:${CI_MERGE_REQUEST_TITLE}\\n合并分支信息:${CI_MERGE_REQUEST_SOURCE_BRANCH_NAME} -> ${CI_MERGE_REQUEST_TARGET_BRANCH_NAME}\\n差异性地址:${DIFF_URL}\\n请及时review代码\"}}" \ - https://open.feishu.cn/open-apis/bot/v2/hook/1c334752-2874-41a1-8f1b-3060f2d46b6c - -prebuild: - stage: prefix - rules: - - if: $CI_COMMIT_BRANCH == "main" - script: - - echo "prebuild" - - chmod +x ./scripts/prefix.sh - - ./scripts/prefix.sh - -builder: - stage: build - rules: - - if: $CI_COMMIT_BRANCH == "main" - script: - - set -e - - | - if [ ! -d "../artifacts" ]; then - mkdir -p ../artifacts - fi - if [ -d "../artifacts/dist" ]; then - cp -r ../artifacts/dist frontend/dist - fi - - | - if [ -n "$(git diff --name-status HEAD~1 HEAD -- frontend)" ]; then - ./scripts/build.sh $BUILD_DIR ${VERSION} all "" - else - ./scripts/build.sh $BUILD_DIR ${VERSION} - fi - if [ -d "frontend/dist" ]; then - echo "copy frontend/dist to artifacts/dist" - rm -fr ../artifacts/dist - cp -r frontend/dist ../artifacts/dist - fi - cp $BUILD_DIR/${APP_PRE}_linux_amd64.tar.gz ${SAVE_DIR} - -deployer: - stage: deploy - rules: - - if: $CI_COMMIT_BRANCH == "main" - variables: - APIPARK_GUEST_MODE: allow - APIPARK_GUEST_ID: dklejrfbhjqwdh - script: - - cd ${SAVE_DIR};mkdir -p ${APP_PRE};tar -zxvf ${APP_PRE}_linux_amd64.tar.gz -C ${APP_PRE};cd ${APP_PRE};./install.sh ${SAVE_DIR};./run.sh restart;cd ${SAVE_DIR} && ./clean.sh ${APP_PRE} - when: on_success -success: - stage: webhook - rules: - - if: $CI_COMMIT_BRANCH == "main" - script: - - | - curl -X POST -H "Content-Type: application/json" \ - -d "{\"msg_type\":\"text\",\"content\":{\"text\":\"最近一次提交:${CI_COMMIT_TITLE}\\n提交人:${GITLAB_USER_NAME}\\n项目:${CI_PROJECT_NAME}\\n环境:${DEPLOY_DESC}\\n更新部署完成.\\n访问地址:${VIEW_ADDR}\\n工作流地址:${CI_PIPELINE_URL}\"}}" \ - https://open.feishu.cn/open-apis/bot/v2/hook/c3672932-4dfa-4989-8023-0128bae59338 - when: on_success -failure: - stage: webhook - rules: - - if: $CI_COMMIT_BRANCH == "main" - script: - - | - curl -X POST -H "Content-Type: application/json" \ - -d "{\"msg_type\":\"text\",\"content\":{\"text\":\"最近一次提交:${CI_COMMIT_TITLE}\\n提交人:${GITLAB_USER_NAME}\\n项目:${CI_PROJECT_NAME}\\n环境:${DEPLOY_DESC}\\n更新部署失败,请及时到gitlab上查看\\n工作流地址:${CI_PIPELINE_URL}\"}}" \ - https://open.feishu.cn/open-apis/bot/v2/hook/c3672932-4dfa-4989-8023-0128bae59338 - when: on_failure diff --git a/ai-provider/local/local.go b/ai-provider/local/local.go index 5e084eeb..bcea9e84 100644 --- a/ai-provider/local/local.go +++ b/ai-provider/local/local.go @@ -8,10 +8,11 @@ import ( ) var ( - client *api.Client + client *api.Client + ProviderLocal = "LocalModel" ) -func ResetOllamaAddress(address string) error { +func ResetLocalAddress(address string) error { u, err := url.Parse(address) if err != nil { return err @@ -19,3 +20,12 @@ func ResetOllamaAddress(address string) error { client = api.NewClient(u, http.DefaultClient) return nil } + +var ( + LocalConfig = "{\n \"temperature\": \"\",\n \"top_p\": \"\",\n \"max_tokens\": \"\"\n}" + LocalSvg = ` + + + +` +) diff --git a/ai-provider/local/models.json b/ai-provider/local/models.json index 74fea8d1..42bc9927 100644 --- a/ai-provider/local/models.json +++ b/ai-provider/local/models.json @@ -10703,7 +10703,7 @@ { "id": "llava", "name": "llava", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "4.7GB", "digest": "8dd30f6b0cb1", "provider": "", @@ -10713,7 +10713,7 @@ { "id": "llava:7b", "name": "llava:7b", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "4.7GB", "digest": "8dd30f6b0cb1", "provider": "", @@ -10723,7 +10723,7 @@ { "id": "llava:13b", "name": "llava:13b", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "8.0GB", "digest": "0d0eb4d7f485", "provider": "", @@ -10733,7 +10733,7 @@ { "id": "llava:34b", "name": "llava:34b", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "20GB", "digest": "3d2d24f46674", "provider": "", @@ -10743,7 +10743,7 @@ { "id": "llava:13b-v1.5-fp16", "name": "llava:13b-v1.5-fp16", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "27GB", "digest": "ce3bde71eaa7", "provider": "", @@ -10753,7 +10753,7 @@ { "id": "llava:13b-v1.5-q2_K", "name": "llava:13b-v1.5-q2_K", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "6.1GB", "digest": "3cdd3869f154", "provider": "", @@ -10763,7 +10763,7 @@ { "id": "llava:13b-v1.5-q3_K_L", "name": "llava:13b-v1.5-q3_K_L", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "7.6GB", "digest": "07b1b74ec398", "provider": "", @@ -10773,7 +10773,7 @@ { "id": "llava:13b-v1.5-q3_K_M", "name": "llava:13b-v1.5-q3_K_M", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "7.0GB", "digest": "61441e82808c", "provider": "", @@ -10783,7 +10783,7 @@ { "id": "llava:13b-v1.5-q3_K_S", "name": "llava:13b-v1.5-q3_K_S", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "6.3GB", "digest": "c860f869008d", "provider": "", @@ -10793,7 +10793,7 @@ { "id": "llava:13b-v1.5-q4_0", "name": "llava:13b-v1.5-q4_0", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "8.0GB", "digest": "e3b7997801dc", "provider": "", @@ -10803,7 +10803,7 @@ { "id": "llava:13b-v1.5-q4_1", "name": "llava:13b-v1.5-q4_1", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "8.8GB", "digest": "0ca30ff66062", "provider": "", @@ -10813,7 +10813,7 @@ { "id": "llava:13b-v1.5-q4_K_M", "name": "llava:13b-v1.5-q4_K_M", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "8.5GB", "digest": "d0a6a3f0e6c4", "provider": "", @@ -10823,7 +10823,7 @@ { "id": "llava:13b-v1.5-q4_K_S", "name": "llava:13b-v1.5-q4_K_S", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "8.1GB", "digest": "16939c152bd9", "provider": "", @@ -10833,7 +10833,7 @@ { "id": "llava:13b-v1.5-q5_0", "name": "llava:13b-v1.5-q5_0", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "9.6GB", "digest": "78e17026912a", "provider": "", @@ -10843,7 +10843,7 @@ { "id": "llava:13b-v1.5-q5_1", "name": "llava:13b-v1.5-q5_1", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "10GB", "digest": "b11f33bc65fa", "provider": "", @@ -10853,7 +10853,7 @@ { "id": "llava:13b-v1.5-q5_K_M", "name": "llava:13b-v1.5-q5_K_M", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "9.9GB", "digest": "48418b116e18", "provider": "", @@ -10863,7 +10863,7 @@ { "id": "llava:13b-v1.5-q5_K_S", "name": "llava:13b-v1.5-q5_K_S", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "9.6GB", "digest": "c7f4b8076a0e", "provider": "", @@ -10873,7 +10873,7 @@ { "id": "llava:13b-v1.5-q6_K", "name": "llava:13b-v1.5-q6_K", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "11GB", "digest": "3c085b55f924", "provider": "", @@ -10883,7 +10883,7 @@ { "id": "llava:13b-v1.5-q8_0", "name": "llava:13b-v1.5-q8_0", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "14GB", "digest": "4e00c435bb25", "provider": "", @@ -10893,7 +10893,7 @@ { "id": "llava:13b-v1.6", "name": "llava:13b-v1.6", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "8.0GB", "digest": "0d0eb4d7f485", "provider": "", @@ -10903,7 +10903,7 @@ { "id": "llava:13b-v1.6-vicuna-fp16", "name": "llava:13b-v1.6-vicuna-fp16", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "27GB", "digest": "81e28406a4e9", "provider": "", @@ -10913,7 +10913,7 @@ { "id": "llava:13b-v1.6-vicuna-q2_K", "name": "llava:13b-v1.6-vicuna-q2_K", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "5.5GB", "digest": "87aecb135e2a", "provider": "", @@ -10923,7 +10923,7 @@ { "id": "llava:13b-v1.6-vicuna-q3_K_L", "name": "llava:13b-v1.6-vicuna-q3_K_L", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "7.6GB", "digest": "a2b56a2b8e79", "provider": "", @@ -10933,7 +10933,7 @@ { "id": "llava:13b-v1.6-vicuna-q3_K_M", "name": "llava:13b-v1.6-vicuna-q3_K_M", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "7.0GB", "digest": "2e48f094b51a", "provider": "", @@ -10943,7 +10943,7 @@ { "id": "llava:13b-v1.6-vicuna-q3_K_S", "name": "llava:13b-v1.6-vicuna-q3_K_S", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "6.3GB", "digest": "54ef03322645", "provider": "", @@ -10953,7 +10953,7 @@ { "id": "llava:13b-v1.6-vicuna-q4_0", "name": "llava:13b-v1.6-vicuna-q4_0", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "8.0GB", "digest": "0d0eb4d7f485", "provider": "", @@ -10963,7 +10963,7 @@ { "id": "llava:13b-v1.6-vicuna-q4_1", "name": "llava:13b-v1.6-vicuna-q4_1", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "8.8GB", "digest": "0b97528b26b0", "provider": "", @@ -10973,7 +10973,7 @@ { "id": "llava:13b-v1.6-vicuna-q4_K_M", "name": "llava:13b-v1.6-vicuna-q4_K_M", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "8.5GB", "digest": "0843119c3874", "provider": "", @@ -10983,7 +10983,7 @@ { "id": "llava:13b-v1.6-vicuna-q4_K_S", "name": "llava:13b-v1.6-vicuna-q4_K_S", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "8.1GB", "digest": "5e4cf96dfc4c", "provider": "", @@ -10993,7 +10993,7 @@ { "id": "llava:13b-v1.6-vicuna-q5_0", "name": "llava:13b-v1.6-vicuna-q5_0", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "9.6GB", "digest": "6e06e1393058", "provider": "", @@ -11003,7 +11003,7 @@ { "id": "llava:13b-v1.6-vicuna-q5_1", "name": "llava:13b-v1.6-vicuna-q5_1", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "10GB", "digest": "31f1d78cb272", "provider": "", @@ -11013,7 +11013,7 @@ { "id": "llava:13b-v1.6-vicuna-q5_K_M", "name": "llava:13b-v1.6-vicuna-q5_K_M", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "9.9GB", "digest": "446b10458a6a", "provider": "", @@ -11023,7 +11023,7 @@ { "id": "llava:13b-v1.6-vicuna-q5_K_S", "name": "llava:13b-v1.6-vicuna-q5_K_S", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "9.6GB", "digest": "53999e2c86c8", "provider": "", @@ -11033,7 +11033,7 @@ { "id": "llava:13b-v1.6-vicuna-q6_K", "name": "llava:13b-v1.6-vicuna-q6_K", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "11GB", "digest": "1c0a91e1e4d9", "provider": "", @@ -11043,7 +11043,7 @@ { "id": "llava:13b-v1.6-vicuna-q8_0", "name": "llava:13b-v1.6-vicuna-q8_0", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "14GB", "digest": "b25c4195f9e2", "provider": "", @@ -11053,7 +11053,7 @@ { "id": "llava:34b-v1.6", "name": "llava:34b-v1.6", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "20GB", "digest": "3d2d24f46674", "provider": "", @@ -11063,7 +11063,7 @@ { "id": "llava:34b-v1.6-fp16", "name": "llava:34b-v1.6-fp16", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "69GB", "digest": "89c50af82d5e", "provider": "", @@ -11073,7 +11073,7 @@ { "id": "llava:34b-v1.6-q2_K", "name": "llava:34b-v1.6-q2_K", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "14GB", "digest": "6326f59da4f1", "provider": "", @@ -11083,7 +11083,7 @@ { "id": "llava:34b-v1.6-q3_K_L", "name": "llava:34b-v1.6-q3_K_L", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "19GB", "digest": "7f9889648d1a", "provider": "", @@ -11093,7 +11093,7 @@ { "id": "llava:34b-v1.6-q3_K_M", "name": "llava:34b-v1.6-q3_K_M", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "17GB", "digest": "89e924fed7d4", "provider": "", @@ -11103,7 +11103,7 @@ { "id": "llava:34b-v1.6-q3_K_S", "name": "llava:34b-v1.6-q3_K_S", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "16GB", "digest": "a0376a205682", "provider": "", @@ -11113,7 +11113,7 @@ { "id": "llava:34b-v1.6-q4_0", "name": "llava:34b-v1.6-q4_0", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "20GB", "digest": "3d2d24f46674", "provider": "", @@ -11123,7 +11123,7 @@ { "id": "llava:34b-v1.6-q4_1", "name": "llava:34b-v1.6-q4_1", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "22GB", "digest": "96d20de28a1a", "provider": "", @@ -11133,7 +11133,7 @@ { "id": "llava:34b-v1.6-q4_K_M", "name": "llava:34b-v1.6-q4_K_M", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "21GB", "digest": "538ff4c5a8b8", "provider": "", @@ -11143,7 +11143,7 @@ { "id": "llava:34b-v1.6-q4_K_S", "name": "llava:34b-v1.6-q4_K_S", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "20GB", "digest": "787b2213f0db", "provider": "", @@ -11153,7 +11153,7 @@ { "id": "llava:34b-v1.6-q5_0", "name": "llava:34b-v1.6-q5_0", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "24GB", "digest": "b239e218bdf0", "provider": "", @@ -11163,7 +11163,7 @@ { "id": "llava:34b-v1.6-q5_1", "name": "llava:34b-v1.6-q5_1", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "27GB", "digest": "60926fd725ec", "provider": "", @@ -11173,7 +11173,7 @@ { "id": "llava:34b-v1.6-q5_K_M", "name": "llava:34b-v1.6-q5_K_M", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "25GB", "digest": "0eb2ab10d35c", "provider": "", @@ -11183,7 +11183,7 @@ { "id": "llava:34b-v1.6-q5_K_S", "name": "llava:34b-v1.6-q5_K_S", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "24GB", "digest": "cdd8d5db3870", "provider": "", @@ -11193,7 +11193,7 @@ { "id": "llava:34b-v1.6-q6_K", "name": "llava:34b-v1.6-q6_K", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "29GB", "digest": "8f572ea02185", "provider": "", @@ -11203,7 +11203,7 @@ { "id": "llava:34b-v1.6-q8_0", "name": "llava:34b-v1.6-q8_0", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "37GB", "digest": "959065f30849", "provider": "", @@ -11213,7 +11213,7 @@ { "id": "llava:7b-v1.5-fp16", "name": "llava:7b-v1.5-fp16", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "14GB", "digest": "337a5b25bada", "provider": "", @@ -11223,7 +11223,7 @@ { "id": "llava:7b-v1.5-q2_K", "name": "llava:7b-v1.5-q2_K", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "3.5GB", "digest": "33973d2589d1", "provider": "", @@ -11233,7 +11233,7 @@ { "id": "llava:7b-v1.5-q3_K_L", "name": "llava:7b-v1.5-q3_K_L", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "4.2GB", "digest": "057fefd59cdb", "provider": "", @@ -11243,7 +11243,7 @@ { "id": "llava:7b-v1.5-q3_K_M", "name": "llava:7b-v1.5-q3_K_M", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "3.9GB", "digest": "9d738df24288", "provider": "", @@ -11253,7 +11253,7 @@ { "id": "llava:7b-v1.5-q3_K_S", "name": "llava:7b-v1.5-q3_K_S", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "3.6GB", "digest": "605b68e0b568", "provider": "", @@ -11263,7 +11263,7 @@ { "id": "llava:7b-v1.5-q4_0", "name": "llava:7b-v1.5-q4_0", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "4.5GB", "digest": "cd3274b81a85", "provider": "", @@ -11273,7 +11273,7 @@ { "id": "llava:7b-v1.5-q4_1", "name": "llava:7b-v1.5-q4_1", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "4.9GB", "digest": "146a55d9df75", "provider": "", @@ -11283,7 +11283,7 @@ { "id": "llava:7b-v1.5-q4_K_M", "name": "llava:7b-v1.5-q4_K_M", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "4.7GB", "digest": "75a9333e75cd", "provider": "", @@ -11293,7 +11293,7 @@ { "id": "llava:7b-v1.5-q4_K_S", "name": "llava:7b-v1.5-q4_K_S", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "4.5GB", "digest": "d8d545afa5f0", "provider": "", @@ -11303,7 +11303,7 @@ { "id": "llava:7b-v1.5-q5_0", "name": "llava:7b-v1.5-q5_0", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "5.3GB", "digest": "339249626980", "provider": "", @@ -11313,7 +11313,7 @@ { "id": "llava:7b-v1.5-q5_1", "name": "llava:7b-v1.5-q5_1", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "5.7GB", "digest": "6d97e8715c53", "provider": "", @@ -11323,7 +11323,7 @@ { "id": "llava:7b-v1.5-q5_K_M", "name": "llava:7b-v1.5-q5_K_M", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "5.4GB", "digest": "4fb097b9cfa3", "provider": "", @@ -11333,7 +11333,7 @@ { "id": "llava:7b-v1.5-q5_K_S", "name": "llava:7b-v1.5-q5_K_S", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "5.3GB", "digest": "34f9b24f2315", "provider": "", @@ -11343,7 +11343,7 @@ { "id": "llava:7b-v1.5-q6_K", "name": "llava:7b-v1.5-q6_K", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "6.2GB", "digest": "df0203e92f79", "provider": "", @@ -11353,7 +11353,7 @@ { "id": "llava:7b-v1.5-q8_0", "name": "llava:7b-v1.5-q8_0", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "7.8GB", "digest": "c684b68b3f34", "provider": "", @@ -11363,7 +11363,7 @@ { "id": "llava:7b-v1.6", "name": "llava:7b-v1.6", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "4.7GB", "digest": "8dd30f6b0cb1", "provider": "", @@ -11373,7 +11373,7 @@ { "id": "llava:7b-v1.6-mistral-fp16", "name": "llava:7b-v1.6-mistral-fp16", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "15GB", "digest": "9fd1e5417c5f", "provider": "", @@ -11383,7 +11383,7 @@ { "id": "llava:7b-v1.6-mistral-q2_K", "name": "llava:7b-v1.6-mistral-q2_K", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "3.3GB", "digest": "52e0ce44a5f5", "provider": "", @@ -11393,7 +11393,7 @@ { "id": "llava:7b-v1.6-mistral-q3_K_L", "name": "llava:7b-v1.6-mistral-q3_K_L", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "4.4GB", "digest": "a48bbc9b567b", "provider": "", @@ -11403,7 +11403,7 @@ { "id": "llava:7b-v1.6-mistral-q3_K_M", "name": "llava:7b-v1.6-mistral-q3_K_M", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "4.1GB", "digest": "25a00600f8b4", "provider": "", @@ -11413,7 +11413,7 @@ { "id": "llava:7b-v1.6-mistral-q3_K_S", "name": "llava:7b-v1.6-mistral-q3_K_S", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "3.8GB", "digest": "2b9c055fe6a2", "provider": "", @@ -11423,7 +11423,7 @@ { "id": "llava:7b-v1.6-mistral-q4_0", "name": "llava:7b-v1.6-mistral-q4_0", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "4.7GB", "digest": "8dd30f6b0cb1", "provider": "", @@ -11433,7 +11433,7 @@ { "id": "llava:7b-v1.6-mistral-q4_1", "name": "llava:7b-v1.6-mistral-q4_1", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "5.2GB", "digest": "8da3213068e6", "provider": "", @@ -11443,7 +11443,7 @@ { "id": "llava:7b-v1.6-mistral-q4_K_M", "name": "llava:7b-v1.6-mistral-q4_K_M", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "5.0GB", "digest": "8d3fbd6ad3f4", "provider": "", @@ -11453,7 +11453,7 @@ { "id": "llava:7b-v1.6-mistral-q4_K_S", "name": "llava:7b-v1.6-mistral-q4_K_S", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "4.8GB", "digest": "2878e8c79f6e", "provider": "", @@ -11463,7 +11463,7 @@ { "id": "llava:7b-v1.6-mistral-q5_0", "name": "llava:7b-v1.6-mistral-q5_0", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "5.6GB", "digest": "b8f63553f521", "provider": "", @@ -11473,7 +11473,7 @@ { "id": "llava:7b-v1.6-mistral-q5_1", "name": "llava:7b-v1.6-mistral-q5_1", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "6.1GB", "digest": "eda0b3f3b09b", "provider": "", @@ -11483,7 +11483,7 @@ { "id": "llava:7b-v1.6-mistral-q5_K_M", "name": "llava:7b-v1.6-mistral-q5_K_M", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "5.8GB", "digest": "244b7e3d3d5a", "provider": "", @@ -11493,7 +11493,7 @@ { "id": "llava:7b-v1.6-mistral-q5_K_S", "name": "llava:7b-v1.6-mistral-q5_K_S", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "5.6GB", "digest": "62dc434a7ae8", "provider": "", @@ -11503,7 +11503,7 @@ { "id": "llava:7b-v1.6-mistral-q6_K", "name": "llava:7b-v1.6-mistral-q6_K", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "6.6GB", "digest": "8781169d7f8f", "provider": "", @@ -11513,7 +11513,7 @@ { "id": "llava:7b-v1.6-mistral-q8_0", "name": "llava:7b-v1.6-mistral-q8_0", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "8.3GB", "digest": "c2973e390e84", "provider": "", @@ -11523,7 +11523,7 @@ { "id": "llava:7b-v1.6-vicuna-fp16", "name": "llava:7b-v1.6-vicuna-fp16", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "14GB", "digest": "bb8da134bacb", "provider": "", @@ -11533,7 +11533,7 @@ { "id": "llava:7b-v1.6-vicuna-q2_K", "name": "llava:7b-v1.6-vicuna-q2_K", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "3.2GB", "digest": "6321163f3833", "provider": "", @@ -11543,7 +11543,7 @@ { "id": "llava:7b-v1.6-vicuna-q3_K_L", "name": "llava:7b-v1.6-vicuna-q3_K_L", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "4.2GB", "digest": "0127d9087e07", "provider": "", @@ -11553,7 +11553,7 @@ { "id": "llava:7b-v1.6-vicuna-q3_K_M", "name": "llava:7b-v1.6-vicuna-q3_K_M", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "3.9GB", "digest": "7004e1f24eb1", "provider": "", @@ -11563,7 +11563,7 @@ { "id": "llava:7b-v1.6-vicuna-q3_K_S", "name": "llava:7b-v1.6-vicuna-q3_K_S", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "3.6GB", "digest": "7701df672950", "provider": "", @@ -11573,7 +11573,7 @@ { "id": "llava:7b-v1.6-vicuna-q4_0", "name": "llava:7b-v1.6-vicuna-q4_0", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "4.5GB", "digest": "b6cbe07f1d5e", "provider": "", @@ -11583,7 +11583,7 @@ { "id": "llava:7b-v1.6-vicuna-q4_1", "name": "llava:7b-v1.6-vicuna-q4_1", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "4.9GB", "digest": "f8a27e237e97", "provider": "", @@ -11593,7 +11593,7 @@ { "id": "llava:7b-v1.6-vicuna-q4_K_M", "name": "llava:7b-v1.6-vicuna-q4_K_M", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "4.7GB", "digest": "15360a9e0fb9", "provider": "", @@ -11603,7 +11603,7 @@ { "id": "llava:7b-v1.6-vicuna-q4_K_S", "name": "llava:7b-v1.6-vicuna-q4_K_S", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "4.5GB", "digest": "5006a8a41d2b", "provider": "", @@ -11613,7 +11613,7 @@ { "id": "llava:7b-v1.6-vicuna-q5_0", "name": "llava:7b-v1.6-vicuna-q5_0", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "5.3GB", "digest": "6a2bb61a611a", "provider": "", @@ -11623,7 +11623,7 @@ { "id": "llava:7b-v1.6-vicuna-q5_1", "name": "llava:7b-v1.6-vicuna-q5_1", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "5.7GB", "digest": "1bd37032ec33", "provider": "", @@ -11633,7 +11633,7 @@ { "id": "llava:7b-v1.6-vicuna-q5_K_M", "name": "llava:7b-v1.6-vicuna-q5_K_M", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "5.4GB", "digest": "b22b0c041223", "provider": "", @@ -11643,7 +11643,7 @@ { "id": "llava:7b-v1.6-vicuna-q5_K_S", "name": "llava:7b-v1.6-vicuna-q5_K_S", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "5.3GB", "digest": "4aaa19502e34", "provider": "", @@ -11653,7 +11653,7 @@ { "id": "llava:7b-v1.6-vicuna-q6_K", "name": "llava:7b-v1.6-vicuna-q6_K", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "6.2GB", "digest": "11bd55683f9c", "provider": "", @@ -11663,7 +11663,7 @@ { "id": "llava:7b-v1.6-vicuna-q8_0", "name": "llava:7b-v1.6-vicuna-q8_0", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "7.8GB", "digest": "6da20a71d9bb", "provider": "", @@ -11673,7 +11673,7 @@ { "id": "llava:v1.6", "name": "llava:v1.6", - "description": "🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", + "description": "LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.", "size": "4.7GB", "digest": "8dd30f6b0cb1", "provider": "", @@ -35753,7 +35753,7 @@ { "id": "smollm", "name": "smollm", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "991MB", "digest": "95f6557a0f0f", "provider": "", @@ -35763,7 +35763,7 @@ { "id": "smollm:135m", "name": "smollm:135m", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "92MB", "digest": "b0b2a4617438", "provider": "", @@ -35773,7 +35773,7 @@ { "id": "smollm:360m", "name": "smollm:360m", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "229MB", "digest": "b3ba1ccba2b8", "provider": "", @@ -35783,7 +35783,7 @@ { "id": "smollm:1.7b", "name": "smollm:1.7b", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "991MB", "digest": "95f6557a0f0f", "provider": "", @@ -35793,7 +35793,7 @@ { "id": "smollm:1.7b-base-v0.2-fp16", "name": "smollm:1.7b-base-v0.2-fp16", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "3.4GB", "digest": "64f31ee84d1c", "provider": "", @@ -35803,7 +35803,7 @@ { "id": "smollm:1.7b-base-v0.2-q2_K", "name": "smollm:1.7b-base-v0.2-q2_K", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "675MB", "digest": "b013da007877", "provider": "", @@ -35813,7 +35813,7 @@ { "id": "smollm:1.7b-base-v0.2-q3_K_L", "name": "smollm:1.7b-base-v0.2-q3_K_L", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "933MB", "digest": "9656463272fe", "provider": "", @@ -35823,7 +35823,7 @@ { "id": "smollm:1.7b-base-v0.2-q3_K_M", "name": "smollm:1.7b-base-v0.2-q3_K_M", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "860MB", "digest": "9acc25818892", "provider": "", @@ -35833,7 +35833,7 @@ { "id": "smollm:1.7b-base-v0.2-q3_K_S", "name": "smollm:1.7b-base-v0.2-q3_K_S", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "777MB", "digest": "596523cb1def", "provider": "", @@ -35843,7 +35843,7 @@ { "id": "smollm:1.7b-base-v0.2-q4_0", "name": "smollm:1.7b-base-v0.2-q4_0", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "991MB", "digest": "97f3f0aef2df", "provider": "", @@ -35853,7 +35853,7 @@ { "id": "smollm:1.7b-base-v0.2-q4_1", "name": "smollm:1.7b-base-v0.2-q4_1", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "1.1GB", "digest": "0c1bb3471f92", "provider": "", @@ -35863,7 +35863,7 @@ { "id": "smollm:1.7b-base-v0.2-q4_K_M", "name": "smollm:1.7b-base-v0.2-q4_K_M", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "1.1GB", "digest": "8dec12ffec4a", "provider": "", @@ -35873,7 +35873,7 @@ { "id": "smollm:1.7b-base-v0.2-q4_K_S", "name": "smollm:1.7b-base-v0.2-q4_K_S", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "999MB", "digest": "9b3d36b6e0c2", "provider": "", @@ -35883,7 +35883,7 @@ { "id": "smollm:1.7b-base-v0.2-q5_0", "name": "smollm:1.7b-base-v0.2-q5_0", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "1.2GB", "digest": "7eddfb294e53", "provider": "", @@ -35893,7 +35893,7 @@ { "id": "smollm:1.7b-base-v0.2-q5_1", "name": "smollm:1.7b-base-v0.2-q5_1", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "1.3GB", "digest": "fde4c91abcce", "provider": "", @@ -35903,7 +35903,7 @@ { "id": "smollm:1.7b-base-v0.2-q5_K_M", "name": "smollm:1.7b-base-v0.2-q5_K_M", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "1.2GB", "digest": "7daaf6f561d8", "provider": "", @@ -35913,7 +35913,7 @@ { "id": "smollm:1.7b-base-v0.2-q5_K_S", "name": "smollm:1.7b-base-v0.2-q5_K_S", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "1.2GB", "digest": "4f0e5ec42d5b", "provider": "", @@ -35923,7 +35923,7 @@ { "id": "smollm:1.7b-base-v0.2-q6_K", "name": "smollm:1.7b-base-v0.2-q6_K", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "1.4GB", "digest": "9cf3be2645b6", "provider": "", @@ -35933,7 +35933,7 @@ { "id": "smollm:1.7b-base-v0.2-q8_0", "name": "smollm:1.7b-base-v0.2-q8_0", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "1.8GB", "digest": "d1f91cd676ad", "provider": "", @@ -35943,7 +35943,7 @@ { "id": "smollm:1.7b-instruct-v0.2-fp16", "name": "smollm:1.7b-instruct-v0.2-fp16", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "3.4GB", "digest": "b07e591ca9ba", "provider": "", @@ -35953,7 +35953,7 @@ { "id": "smollm:1.7b-instruct-v0.2-q2_K", "name": "smollm:1.7b-instruct-v0.2-q2_K", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "675MB", "digest": "da8ba92e42b1", "provider": "", @@ -35963,7 +35963,7 @@ { "id": "smollm:1.7b-instruct-v0.2-q3_K_L", "name": "smollm:1.7b-instruct-v0.2-q3_K_L", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "933MB", "digest": "88ef3062260e", "provider": "", @@ -35973,7 +35973,7 @@ { "id": "smollm:1.7b-instruct-v0.2-q3_K_M", "name": "smollm:1.7b-instruct-v0.2-q3_K_M", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "860MB", "digest": "728c1f932b06", "provider": "", @@ -35983,7 +35983,7 @@ { "id": "smollm:1.7b-instruct-v0.2-q3_K_S", "name": "smollm:1.7b-instruct-v0.2-q3_K_S", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "777MB", "digest": "c35438da7180", "provider": "", @@ -35993,7 +35993,7 @@ { "id": "smollm:1.7b-instruct-v0.2-q4_0", "name": "smollm:1.7b-instruct-v0.2-q4_0", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "991MB", "digest": "95f6557a0f0f", "provider": "", @@ -36003,7 +36003,7 @@ { "id": "smollm:1.7b-instruct-v0.2-q4_1", "name": "smollm:1.7b-instruct-v0.2-q4_1", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "1.1GB", "digest": "77d7238b232e", "provider": "", @@ -36013,7 +36013,7 @@ { "id": "smollm:1.7b-instruct-v0.2-q4_K_M", "name": "smollm:1.7b-instruct-v0.2-q4_K_M", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "1.1GB", "digest": "dd7299829a14", "provider": "", @@ -36023,7 +36023,7 @@ { "id": "smollm:1.7b-instruct-v0.2-q4_K_S", "name": "smollm:1.7b-instruct-v0.2-q4_K_S", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "999MB", "digest": "35eec1a7014d", "provider": "", @@ -36033,7 +36033,7 @@ { "id": "smollm:1.7b-instruct-v0.2-q5_0", "name": "smollm:1.7b-instruct-v0.2-q5_0", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "1.2GB", "digest": "8391060f29db", "provider": "", @@ -36043,7 +36043,7 @@ { "id": "smollm:1.7b-instruct-v0.2-q5_1", "name": "smollm:1.7b-instruct-v0.2-q5_1", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "1.3GB", "digest": "ce91dd1d7380", "provider": "", @@ -36053,7 +36053,7 @@ { "id": "smollm:1.7b-instruct-v0.2-q5_K_M", "name": "smollm:1.7b-instruct-v0.2-q5_K_M", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "1.2GB", "digest": "91d16f72a7b3", "provider": "", @@ -36063,7 +36063,7 @@ { "id": "smollm:1.7b-instruct-v0.2-q5_K_S", "name": "smollm:1.7b-instruct-v0.2-q5_K_S", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "1.2GB", "digest": "489a893ac8a7", "provider": "", @@ -36073,7 +36073,7 @@ { "id": "smollm:1.7b-instruct-v0.2-q6_K", "name": "smollm:1.7b-instruct-v0.2-q6_K", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "1.4GB", "digest": "ecdf9864899c", "provider": "", @@ -36083,7 +36083,7 @@ { "id": "smollm:1.7b-instruct-v0.2-q8_0", "name": "smollm:1.7b-instruct-v0.2-q8_0", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "1.8GB", "digest": "4afeb8de9eec", "provider": "", @@ -36093,7 +36093,7 @@ { "id": "smollm:135m-base-v0.2-fp16", "name": "smollm:135m-base-v0.2-fp16", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "271MB", "digest": "1d0f00cec918", "provider": "", @@ -36103,7 +36103,7 @@ { "id": "smollm:135m-base-v0.2-q2_K", "name": "smollm:135m-base-v0.2-q2_K", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "88MB", "digest": "e2048e8ac78d", "provider": "", @@ -36113,7 +36113,7 @@ { "id": "smollm:135m-base-v0.2-q3_K_L", "name": "smollm:135m-base-v0.2-q3_K_L", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "98MB", "digest": "c2e22813067a", "provider": "", @@ -36123,7 +36123,7 @@ { "id": "smollm:135m-base-v0.2-q3_K_M", "name": "smollm:135m-base-v0.2-q3_K_M", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "94MB", "digest": "a5ab45ae2e2e", "provider": "", @@ -36133,7 +36133,7 @@ { "id": "smollm:135m-base-v0.2-q3_K_S", "name": "smollm:135m-base-v0.2-q3_K_S", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "88MB", "digest": "102433ffd9fb", "provider": "", @@ -36143,7 +36143,7 @@ { "id": "smollm:135m-base-v0.2-q4_0", "name": "smollm:135m-base-v0.2-q4_0", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "92MB", "digest": "528415e872e5", "provider": "", @@ -36153,7 +36153,7 @@ { "id": "smollm:135m-base-v0.2-q4_1", "name": "smollm:135m-base-v0.2-q4_1", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "98MB", "digest": "edecca7cff90", "provider": "", @@ -36163,7 +36163,7 @@ { "id": "smollm:135m-base-v0.2-q4_K_M", "name": "smollm:135m-base-v0.2-q4_K_M", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "105MB", "digest": "3177ecf5d3b5", "provider": "", @@ -36173,7 +36173,7 @@ { "id": "smollm:135m-base-v0.2-q4_K_S", "name": "smollm:135m-base-v0.2-q4_K_S", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "102MB", "digest": "2aa760d22716", "provider": "", @@ -36183,7 +36183,7 @@ { "id": "smollm:135m-base-v0.2-q5_0", "name": "smollm:135m-base-v0.2-q5_0", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "105MB", "digest": "825973a56945", "provider": "", @@ -36193,7 +36193,7 @@ { "id": "smollm:135m-base-v0.2-q5_1", "name": "smollm:135m-base-v0.2-q5_1", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "112MB", "digest": "065d585f86d9", "provider": "", @@ -36203,7 +36203,7 @@ { "id": "smollm:135m-base-v0.2-q5_K_M", "name": "smollm:135m-base-v0.2-q5_K_M", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "112MB", "digest": "34545da0aa92", "provider": "", @@ -36213,7 +36213,7 @@ { "id": "smollm:135m-base-v0.2-q5_K_S", "name": "smollm:135m-base-v0.2-q5_K_S", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "110MB", "digest": "4aef8aaa4469", "provider": "", @@ -36223,7 +36223,7 @@ { "id": "smollm:135m-base-v0.2-q6_K", "name": "smollm:135m-base-v0.2-q6_K", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "138MB", "digest": "b11338fff65a", "provider": "", @@ -36233,7 +36233,7 @@ { "id": "smollm:135m-base-v0.2-q8_0", "name": "smollm:135m-base-v0.2-q8_0", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "145MB", "digest": "fcdc33f9909d", "provider": "", @@ -36243,7 +36243,7 @@ { "id": "smollm:135m-instruct-v0.2-fp16", "name": "smollm:135m-instruct-v0.2-fp16", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "271MB", "digest": "95d01a081beb", "provider": "", @@ -36253,7 +36253,7 @@ { "id": "smollm:135m-instruct-v0.2-q2_K", "name": "smollm:135m-instruct-v0.2-q2_K", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "88MB", "digest": "e14a680b0b22", "provider": "", @@ -36263,7 +36263,7 @@ { "id": "smollm:135m-instruct-v0.2-q3_K_L", "name": "smollm:135m-instruct-v0.2-q3_K_L", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "98MB", "digest": "aede666c7e16", "provider": "", @@ -36273,7 +36273,7 @@ { "id": "smollm:135m-instruct-v0.2-q3_K_M", "name": "smollm:135m-instruct-v0.2-q3_K_M", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "94MB", "digest": "f5db1ab329b1", "provider": "", @@ -36283,7 +36283,7 @@ { "id": "smollm:135m-instruct-v0.2-q3_K_S", "name": "smollm:135m-instruct-v0.2-q3_K_S", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "88MB", "digest": "3ae895b5e9ea", "provider": "", @@ -36293,7 +36293,7 @@ { "id": "smollm:135m-instruct-v0.2-q4_0", "name": "smollm:135m-instruct-v0.2-q4_0", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "92MB", "digest": "b0b2a4617438", "provider": "", @@ -36303,7 +36303,7 @@ { "id": "smollm:135m-instruct-v0.2-q4_1", "name": "smollm:135m-instruct-v0.2-q4_1", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "98MB", "digest": "bbf7e14150a9", "provider": "", @@ -36313,7 +36313,7 @@ { "id": "smollm:135m-instruct-v0.2-q4_K_M", "name": "smollm:135m-instruct-v0.2-q4_K_M", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "105MB", "digest": "7eedbf45baa1", "provider": "", @@ -36323,7 +36323,7 @@ { "id": "smollm:135m-instruct-v0.2-q4_K_S", "name": "smollm:135m-instruct-v0.2-q4_K_S", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "102MB", "digest": "45013939c7c9", "provider": "", @@ -36333,7 +36333,7 @@ { "id": "smollm:135m-instruct-v0.2-q5_0", "name": "smollm:135m-instruct-v0.2-q5_0", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "105MB", "digest": "33187e134422", "provider": "", @@ -36343,7 +36343,7 @@ { "id": "smollm:135m-instruct-v0.2-q5_1", "name": "smollm:135m-instruct-v0.2-q5_1", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "112MB", "digest": "51f73e9aa3f5", "provider": "", @@ -36353,7 +36353,7 @@ { "id": "smollm:135m-instruct-v0.2-q5_K_M", "name": "smollm:135m-instruct-v0.2-q5_K_M", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "112MB", "digest": "a8975f4dc03b", "provider": "", @@ -36363,7 +36363,7 @@ { "id": "smollm:135m-instruct-v0.2-q5_K_S", "name": "smollm:135m-instruct-v0.2-q5_K_S", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "110MB", "digest": "52d3fabb63ee", "provider": "", @@ -36373,7 +36373,7 @@ { "id": "smollm:135m-instruct-v0.2-q6_K", "name": "smollm:135m-instruct-v0.2-q6_K", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "138MB", "digest": "dd1e60cf54df", "provider": "", @@ -36383,7 +36383,7 @@ { "id": "smollm:135m-instruct-v0.2-q8_0", "name": "smollm:135m-instruct-v0.2-q8_0", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "145MB", "digest": "6fbf8e918862", "provider": "", @@ -36393,7 +36393,7 @@ { "id": "smollm:360m-base-v0.2-fp16", "name": "smollm:360m-base-v0.2-fp16", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "726MB", "digest": "417c81308b94", "provider": "", @@ -36403,7 +36403,7 @@ { "id": "smollm:360m-base-v0.2-q2_K", "name": "smollm:360m-base-v0.2-q2_K", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "219MB", "digest": "e5e1a58c7d0c", "provider": "", @@ -36413,7 +36413,7 @@ { "id": "smollm:360m-base-v0.2-q3_K_L", "name": "smollm:360m-base-v0.2-q3_K_L", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "246MB", "digest": "d13db1d8af29", "provider": "", @@ -36423,7 +36423,7 @@ { "id": "smollm:360m-base-v0.2-q3_K_M", "name": "smollm:360m-base-v0.2-q3_K_M", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "235MB", "digest": "66875ceb33a7", "provider": "", @@ -36433,7 +36433,7 @@ { "id": "smollm:360m-base-v0.2-q3_K_S", "name": "smollm:360m-base-v0.2-q3_K_S", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "219MB", "digest": "8aba991b34bf", "provider": "", @@ -36443,7 +36443,7 @@ { "id": "smollm:360m-base-v0.2-q4_0", "name": "smollm:360m-base-v0.2-q4_0", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "229MB", "digest": "32ed02cfe24e", "provider": "", @@ -36453,7 +36453,7 @@ { "id": "smollm:360m-base-v0.2-q4_1", "name": "smollm:360m-base-v0.2-q4_1", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "249MB", "digest": "74c8ae01b04d", "provider": "", @@ -36463,7 +36463,7 @@ { "id": "smollm:360m-base-v0.2-q4_K_M", "name": "smollm:360m-base-v0.2-q4_K_M", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "271MB", "digest": "d3b4961678c3", "provider": "", @@ -36473,7 +36473,7 @@ { "id": "smollm:360m-base-v0.2-q4_K_S", "name": "smollm:360m-base-v0.2-q4_K_S", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "260MB", "digest": "81b950fac810", "provider": "", @@ -36483,7 +36483,7 @@ { "id": "smollm:360m-base-v0.2-q5_0", "name": "smollm:360m-base-v0.2-q5_0", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "268MB", "digest": "319a822c1baa", "provider": "", @@ -36493,7 +36493,7 @@ { "id": "smollm:360m-base-v0.2-q5_1", "name": "smollm:360m-base-v0.2-q5_1", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "288MB", "digest": "1c350d9f5c43", "provider": "", @@ -36503,7 +36503,7 @@ { "id": "smollm:360m-base-v0.2-q5_K_M", "name": "smollm:360m-base-v0.2-q5_K_M", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "290MB", "digest": "bf2e3336142d", "provider": "", @@ -36513,7 +36513,7 @@ { "id": "smollm:360m-base-v0.2-q5_K_S", "name": "smollm:360m-base-v0.2-q5_K_S", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "283MB", "digest": "05dad743d112", "provider": "", @@ -36523,7 +36523,7 @@ { "id": "smollm:360m-base-v0.2-q6_K", "name": "smollm:360m-base-v0.2-q6_K", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "367MB", "digest": "6006320b3839", "provider": "", @@ -36533,7 +36533,7 @@ { "id": "smollm:360m-base-v0.2-q8_0", "name": "smollm:360m-base-v0.2-q8_0", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "386MB", "digest": "dd417453fb49", "provider": "", @@ -36543,7 +36543,7 @@ { "id": "smollm:360m-instruct-v0.2-fp16", "name": "smollm:360m-instruct-v0.2-fp16", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "726MB", "digest": "d0475be06ee3", "provider": "", @@ -36553,7 +36553,7 @@ { "id": "smollm:360m-instruct-v0.2-q2_K", "name": "smollm:360m-instruct-v0.2-q2_K", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "219MB", "digest": "1dfe94149c4d", "provider": "", @@ -36563,7 +36563,7 @@ { "id": "smollm:360m-instruct-v0.2-q3_K_L", "name": "smollm:360m-instruct-v0.2-q3_K_L", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "246MB", "digest": "6f711219bd5f", "provider": "", @@ -36573,7 +36573,7 @@ { "id": "smollm:360m-instruct-v0.2-q3_K_M", "name": "smollm:360m-instruct-v0.2-q3_K_M", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "235MB", "digest": "4ecfcbc87975", "provider": "", @@ -36583,7 +36583,7 @@ { "id": "smollm:360m-instruct-v0.2-q3_K_S", "name": "smollm:360m-instruct-v0.2-q3_K_S", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "219MB", "digest": "359f743b44a1", "provider": "", @@ -36593,7 +36593,7 @@ { "id": "smollm:360m-instruct-v0.2-q4_0", "name": "smollm:360m-instruct-v0.2-q4_0", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "229MB", "digest": "b3ba1ccba2b8", "provider": "", @@ -36603,7 +36603,7 @@ { "id": "smollm:360m-instruct-v0.2-q4_1", "name": "smollm:360m-instruct-v0.2-q4_1", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "249MB", "digest": "4a28c336aedd", "provider": "", @@ -36613,7 +36613,7 @@ { "id": "smollm:360m-instruct-v0.2-q4_K_M", "name": "smollm:360m-instruct-v0.2-q4_K_M", - "description": "🪐 A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", + "description": "A family of small models with 135M, 360M, and 1.7B parameters, trained on a new high-quality dataset.", "size": "271MB", "digest": "535cb302b6b9", "provider": "", @@ -43183,7 +43183,7 @@ { "id": "dolphin3", "name": "dolphin3", - "description": "Dolphin 3.0 Llama 3.1 8B 🐬 is the next generation of the Dolphin series of instruct-tuned models designed to be the ultimate general purpose local model, enabling coding, math, agentic, function calling, and general use cases.", + "description": "Dolphin 3.0 Llama 3.1 8B is the next generation of the Dolphin series of instruct-tuned models designed to be the ultimate general purpose local model, enabling coding, math, agentic, function calling, and general use cases.", "size": "4.9GB", "digest": "d5ab9ae8e1f2", "provider": "", @@ -43193,7 +43193,7 @@ { "id": "dolphin3:8b", "name": "dolphin3:8b", - "description": "Dolphin 3.0 Llama 3.1 8B 🐬 is the next generation of the Dolphin series of instruct-tuned models designed to be the ultimate general purpose local model, enabling coding, math, agentic, function calling, and general use cases.", + "description": "Dolphin 3.0 Llama 3.1 8B is the next generation of the Dolphin series of instruct-tuned models designed to be the ultimate general purpose local model, enabling coding, math, agentic, function calling, and general use cases.", "size": "4.9GB", "digest": "d5ab9ae8e1f2", "provider": "", @@ -43203,7 +43203,7 @@ { "id": "dolphin3:8b-llama3.1-fp16", "name": "dolphin3:8b-llama3.1-fp16", - "description": "Dolphin 3.0 Llama 3.1 8B 🐬 is the next generation of the Dolphin series of instruct-tuned models designed to be the ultimate general purpose local model, enabling coding, math, agentic, function calling, and general use cases.", + "description": "Dolphin 3.0 Llama 3.1 8B is the next generation of the Dolphin series of instruct-tuned models designed to be the ultimate general purpose local model, enabling coding, math, agentic, function calling, and general use cases.", "size": "16GB", "digest": "b0941c6f3226", "provider": "", @@ -43213,7 +43213,7 @@ { "id": "dolphin3:8b-llama3.1-q4_K_M", "name": "dolphin3:8b-llama3.1-q4_K_M", - "description": "Dolphin 3.0 Llama 3.1 8B 🐬 is the next generation of the Dolphin series of instruct-tuned models designed to be the ultimate general purpose local model, enabling coding, math, agentic, function calling, and general use cases.", + "description": "Dolphin 3.0 Llama 3.1 8B is the next generation of the Dolphin series of instruct-tuned models designed to be the ultimate general purpose local model, enabling coding, math, agentic, function calling, and general use cases.", "size": "4.9GB", "digest": "d5ab9ae8e1f2", "provider": "", @@ -43223,7 +43223,7 @@ { "id": "dolphin3:8b-llama3.1-q8_0", "name": "dolphin3:8b-llama3.1-q8_0", - "description": "Dolphin 3.0 Llama 3.1 8B 🐬 is the next generation of the Dolphin series of instruct-tuned models designed to be the ultimate general purpose local model, enabling coding, math, agentic, function calling, and general use cases.", + "description": "Dolphin 3.0 Llama 3.1 8B is the next generation of the Dolphin series of instruct-tuned models designed to be the ultimate general purpose local model, enabling coding, math, agentic, function calling, and general use cases.", "size": "8.5GB", "digest": "e3310b61ffdb", "provider": "", @@ -59063,7 +59063,7 @@ { "id": "magicoder", "name": "magicoder", - "description": "🎩 Magicoder is a family of 7B parameter models trained on 75K synthetic instruction data using OSS-Instruct, a novel approach to enlightening LLMs with open-source code snippets.", + "description": "Magicoder is a family of 7B parameter models trained on 75K synthetic instruction data using OSS-Instruct, a novel approach to enlightening LLMs with open-source code snippets.", "size": "3.8GB", "digest": "8007de06f5d9", "provider": "", @@ -59073,7 +59073,7 @@ { "id": "magicoder:7b", "name": "magicoder:7b", - "description": "🎩 Magicoder is a family of 7B parameter models trained on 75K synthetic instruction data using OSS-Instruct, a novel approach to enlightening LLMs with open-source code snippets.", + "description": "Magicoder is a family of 7B parameter models trained on 75K synthetic instruction data using OSS-Instruct, a novel approach to enlightening LLMs with open-source code snippets.", "size": "3.8GB", "digest": "8007de06f5d9", "provider": "", @@ -59083,7 +59083,7 @@ { "id": "magicoder:7b-s-cl", "name": "magicoder:7b-s-cl", - "description": "🎩 Magicoder is a family of 7B parameter models trained on 75K synthetic instruction data using OSS-Instruct, a novel approach to enlightening LLMs with open-source code snippets.", + "description": "Magicoder is a family of 7B parameter models trained on 75K synthetic instruction data using OSS-Instruct, a novel approach to enlightening LLMs with open-source code snippets.", "size": "3.8GB", "digest": "8007de06f5d9", "provider": "", @@ -59093,7 +59093,7 @@ { "id": "magicoder:7b-s-cl-fp16", "name": "magicoder:7b-s-cl-fp16", - "description": "🎩 Magicoder is a family of 7B parameter models trained on 75K synthetic instruction data using OSS-Instruct, a novel approach to enlightening LLMs with open-source code snippets.", + "description": "Magicoder is a family of 7B parameter models trained on 75K synthetic instruction data using OSS-Instruct, a novel approach to enlightening LLMs with open-source code snippets.", "size": "13GB", "digest": "859e1ae4f566", "provider": "", @@ -59103,7 +59103,7 @@ { "id": "magicoder:7b-s-cl-q2_K", "name": "magicoder:7b-s-cl-q2_K", - "description": "🎩 Magicoder is a family of 7B parameter models trained on 75K synthetic instruction data using OSS-Instruct, a novel approach to enlightening LLMs with open-source code snippets.", + "description": "Magicoder is a family of 7B parameter models trained on 75K synthetic instruction data using OSS-Instruct, a novel approach to enlightening LLMs with open-source code snippets.", "size": "2.8GB", "digest": "86fd329c64b6", "provider": "", @@ -59113,7 +59113,7 @@ { "id": "magicoder:7b-s-cl-q3_K_L", "name": "magicoder:7b-s-cl-q3_K_L", - "description": "🎩 Magicoder is a family of 7B parameter models trained on 75K synthetic instruction data using OSS-Instruct, a novel approach to enlightening LLMs with open-source code snippets.", + "description": "Magicoder is a family of 7B parameter models trained on 75K synthetic instruction data using OSS-Instruct, a novel approach to enlightening LLMs with open-source code snippets.", "size": "3.6GB", "digest": "2bf1232fd9c1", "provider": "", @@ -59123,7 +59123,7 @@ { "id": "magicoder:7b-s-cl-q3_K_M", "name": "magicoder:7b-s-cl-q3_K_M", - "description": "🎩 Magicoder is a family of 7B parameter models trained on 75K synthetic instruction data using OSS-Instruct, a novel approach to enlightening LLMs with open-source code snippets.", + "description": "Magicoder is a family of 7B parameter models trained on 75K synthetic instruction data using OSS-Instruct, a novel approach to enlightening LLMs with open-source code snippets.", "size": "3.3GB", "digest": "b2a3d9987ff3", "provider": "", @@ -59133,7 +59133,7 @@ { "id": "magicoder:7b-s-cl-q3_K_S", "name": "magicoder:7b-s-cl-q3_K_S", - "description": "🎩 Magicoder is a family of 7B parameter models trained on 75K synthetic instruction data using OSS-Instruct, a novel approach to enlightening LLMs with open-source code snippets.", + "description": "Magicoder is a family of 7B parameter models trained on 75K synthetic instruction data using OSS-Instruct, a novel approach to enlightening LLMs with open-source code snippets.", "size": "2.9GB", "digest": "b7e2d7a303f8", "provider": "", @@ -59143,7 +59143,7 @@ { "id": "magicoder:7b-s-cl-q4_0", "name": "magicoder:7b-s-cl-q4_0", - "description": "🎩 Magicoder is a family of 7B parameter models trained on 75K synthetic instruction data using OSS-Instruct, a novel approach to enlightening LLMs with open-source code snippets.", + "description": "Magicoder is a family of 7B parameter models trained on 75K synthetic instruction data using OSS-Instruct, a novel approach to enlightening LLMs with open-source code snippets.", "size": "3.8GB", "digest": "8007de06f5d9", "provider": "", @@ -59153,7 +59153,7 @@ { "id": "magicoder:7b-s-cl-q4_1", "name": "magicoder:7b-s-cl-q4_1", - "description": "🎩 Magicoder is a family of 7B parameter models trained on 75K synthetic instruction data using OSS-Instruct, a novel approach to enlightening LLMs with open-source code snippets.", + "description": "Magicoder is a family of 7B parameter models trained on 75K synthetic instruction data using OSS-Instruct, a novel approach to enlightening LLMs with open-source code snippets.", "size": "4.2GB", "digest": "b29bf322a686", "provider": "", @@ -59163,7 +59163,7 @@ { "id": "magicoder:7b-s-cl-q4_K_M", "name": "magicoder:7b-s-cl-q4_K_M", - "description": "🎩 Magicoder is a family of 7B parameter models trained on 75K synthetic instruction data using OSS-Instruct, a novel approach to enlightening LLMs with open-source code snippets.", + "description": "Magicoder is a family of 7B parameter models trained on 75K synthetic instruction data using OSS-Instruct, a novel approach to enlightening LLMs with open-source code snippets.", "size": "4.1GB", "digest": "902b6b028113", "provider": "", @@ -59173,7 +59173,7 @@ { "id": "magicoder:7b-s-cl-q4_K_S", "name": "magicoder:7b-s-cl-q4_K_S", - "description": "🎩 Magicoder is a family of 7B parameter models trained on 75K synthetic instruction data using OSS-Instruct, a novel approach to enlightening LLMs with open-source code snippets.", + "description": "Magicoder is a family of 7B parameter models trained on 75K synthetic instruction data using OSS-Instruct, a novel approach to enlightening LLMs with open-source code snippets.", "size": "3.9GB", "digest": "42b56dd9b1da", "provider": "", @@ -59183,7 +59183,7 @@ { "id": "magicoder:7b-s-cl-q5_0", "name": "magicoder:7b-s-cl-q5_0", - "description": "🎩 Magicoder is a family of 7B parameter models trained on 75K synthetic instruction data using OSS-Instruct, a novel approach to enlightening LLMs with open-source code snippets.", + "description": "Magicoder is a family of 7B parameter models trained on 75K synthetic instruction data using OSS-Instruct, a novel approach to enlightening LLMs with open-source code snippets.", "size": "4.7GB", "digest": "10fa66c827ee", "provider": "", @@ -59193,7 +59193,7 @@ { "id": "magicoder:7b-s-cl-q5_1", "name": "magicoder:7b-s-cl-q5_1", - "description": "🎩 Magicoder is a family of 7B parameter models trained on 75K synthetic instruction data using OSS-Instruct, a novel approach to enlightening LLMs with open-source code snippets.", + "description": "Magicoder is a family of 7B parameter models trained on 75K synthetic instruction data using OSS-Instruct, a novel approach to enlightening LLMs with open-source code snippets.", "size": "5.1GB", "digest": "7da1fdad5251", "provider": "", @@ -59203,7 +59203,7 @@ { "id": "magicoder:7b-s-cl-q5_K_M", "name": "magicoder:7b-s-cl-q5_K_M", - "description": "🎩 Magicoder is a family of 7B parameter models trained on 75K synthetic instruction data using OSS-Instruct, a novel approach to enlightening LLMs with open-source code snippets.", + "description": "Magicoder is a family of 7B parameter models trained on 75K synthetic instruction data using OSS-Instruct, a novel approach to enlightening LLMs with open-source code snippets.", "size": "4.8GB", "digest": "057c4f587f41", "provider": "", @@ -59213,7 +59213,7 @@ { "id": "magicoder:7b-s-cl-q5_K_S", "name": "magicoder:7b-s-cl-q5_K_S", - "description": "🎩 Magicoder is a family of 7B parameter models trained on 75K synthetic instruction data using OSS-Instruct, a novel approach to enlightening LLMs with open-source code snippets.", + "description": "Magicoder is a family of 7B parameter models trained on 75K synthetic instruction data using OSS-Instruct, a novel approach to enlightening LLMs with open-source code snippets.", "size": "4.7GB", "digest": "ce0b81d40da6", "provider": "", @@ -59223,7 +59223,7 @@ { "id": "magicoder:7b-s-cl-q6_K", "name": "magicoder:7b-s-cl-q6_K", - "description": "🎩 Magicoder is a family of 7B parameter models trained on 75K synthetic instruction data using OSS-Instruct, a novel approach to enlightening LLMs with open-source code snippets.", + "description": "Magicoder is a family of 7B parameter models trained on 75K synthetic instruction data using OSS-Instruct, a novel approach to enlightening LLMs with open-source code snippets.", "size": "5.5GB", "digest": "cf853951ae4a", "provider": "", @@ -59233,7 +59233,7 @@ { "id": "magicoder:7b-s-cl-q8_0", "name": "magicoder:7b-s-cl-q8_0", - "description": "🎩 Magicoder is a family of 7B parameter models trained on 75K synthetic instruction data using OSS-Instruct, a novel approach to enlightening LLMs with open-source code snippets.", + "description": "Magicoder is a family of 7B parameter models trained on 75K synthetic instruction data using OSS-Instruct, a novel approach to enlightening LLMs with open-source code snippets.", "size": "7.2GB", "digest": "96b5e4193464", "provider": "", diff --git a/ai-provider/local/ollama.go b/ai-provider/local/ollama.go deleted file mode 100644 index be765848..00000000 --- a/ai-provider/local/ollama.go +++ /dev/null @@ -1,102 +0,0 @@ -package ai_provider_local - -var ( - OllamaConfig = "{\n \"mirostat\": 0,\n \"mirostat_eta\": 0.1,\n \"mirostat_tau\": 5.0,\n \"num_ctx\": 4096,\n \"repeat_last_n\":64,\n \"repeat_penalty\": 1.1,\n \"temperature\": 0.7,\n \"seed\": 42,\n \"num_predict\": 42,\n \"top_k\": 40,\n \"top_p\": 0.9,\n \"min_p\": 0.5\n}\n" - OllamaSvg = ` - - - - - - - - - - - -` -) diff --git a/ai-provider/model-runtime/model-providers/authropic/anthropic.yaml b/ai-provider/model-runtime/model-providers/authropic/anthropic.yaml index f44d3e18..7bcaf986 100644 --- a/ai-provider/model-runtime/model-providers/authropic/anthropic.yaml +++ b/ai-provider/model-runtime/model-providers/authropic/anthropic.yaml @@ -31,12 +31,12 @@ provider_credential_schema: en_US: Enter your API Key - variable: anthropic_api_url label: - en_US: API URL + en_US: https://api.anthropic.com/v1/ type: text-input required: false placeholder: zh_Hans: 在此输入您的 API URL en_US: Enter your API URL -address: https://api.anthropic.com +address: https://api.anthropic.com/v1/ recommend: true sort: 2 \ No newline at end of file diff --git a/ai-provider/model-runtime/model-providers/baichuan/baichuan.yaml b/ai-provider/model-runtime/model-providers/baichuan/baichuan.yaml index dcadb657..da814abd 100644 --- a/ai-provider/model-runtime/model-providers/baichuan/baichuan.yaml +++ b/ai-provider/model-runtime/model-providers/baichuan/baichuan.yaml @@ -27,4 +27,12 @@ provider_credential_schema: placeholder: zh_Hans: 在此输入您的 API Key en_US: Enter your API Key -address: https://api.baichuan-ai.com + - variable: base_url + label: + en_US: https://api.baichuan-ai.com/v1 + type: text-input + required: false + placeholder: + zh_Hans: 在此输入您的 Base URL + en_US: Enter your Base URL +address: https://api.baichuan-ai.com/v1 diff --git a/ai-provider/model-runtime/model-providers/bailian/bailian.yaml b/ai-provider/model-runtime/model-providers/bailian/bailian.yaml index 7456a7de..93bd6a02 100644 --- a/ai-provider/model-runtime/model-providers/bailian/bailian.yaml +++ b/ai-provider/model-runtime/model-providers/bailian/bailian.yaml @@ -1,7 +1,7 @@ provider: bailian label: zh_Hans: 阿里云百炼 - en_US: bailian + en_US: BaiLian icon_small: en_US: icon_s_en.svg icon_large: @@ -21,7 +21,7 @@ configurate_methods: - customizable-model provider_credential_schema: credential_form_schemas: - - variable: dashscope_api_key + - variable: api_key label: en_US: API Key type: secret-input @@ -29,4 +29,12 @@ provider_credential_schema: placeholder: zh_Hans: 在此输入您的 API Key en_US: Enter your API Key -address: https://dashscope.aliyuncs.com \ No newline at end of file + - variable: base_url + label: + en_US: https://dashscope.aliyuncs.com/compatible-mode/v1 + type: text-input + required: false + placeholder: + zh_Hans: 在此输入您的 Base URL + en_US: Enter your Base URL +address: https://dashscope.aliyuncs.com/compatible-mode/v1 \ No newline at end of file diff --git a/ai-provider/model-runtime/model-providers/bedrock/bedrock.yaml b/ai-provider/model-runtime/model-providers/bedrock/bedrock.yaml index b9f165c2..f7332e40 100644 --- a/ai-provider/model-runtime/model-providers/bedrock/bedrock.yaml +++ b/ai-provider/model-runtime/model-providers/bedrock/bedrock.yaml @@ -87,6 +87,9 @@ provider_credential_schema: placeholder: en_US: A model you have access to (e.g. amazon.titan-text-lite-v1) for validation. zh_Hans: 为了进行验证,请输入一个您可用的模型名称 (例如:amazon.titan-text-lite-v1) +model_config: + access_configuration_status: true + access_configuration_demo: "{\"region\":\"\",\"model\":\"\"}" address: https://bedrock-runtime.amazonaws.com sort: 4 recommend: true \ No newline at end of file diff --git a/ai-provider/model-runtime/model-providers/chatglm/chatglm.yaml b/ai-provider/model-runtime/model-providers/chatglm/chatglm.yaml index 715febcd..25ee59f2 100644 --- a/ai-provider/model-runtime/model-providers/chatglm/chatglm.yaml +++ b/ai-provider/model-runtime/model-providers/chatglm/chatglm.yaml @@ -18,12 +18,20 @@ configurate_methods: - predefined-model provider_credential_schema: credential_form_schemas: - - variable: api_base + - variable: api_key label: - en_US: API URL - type: text-input + en_US: API Key + type: secret-input required: true + placeholder: + zh_Hans: 在此输入您的 API Key + en_US: Enter your API Key + - variable: base_url + label: + en_US: https://open.bigmodel.cn/api/paas/v4/ + type: text-input + required: false placeholder: zh_Hans: 在此输入您的 API URL en_US: Enter your API URL -address: https://api.openai.com \ No newline at end of file +address: https://open.bigmodel.cn/api/paas/v4/ \ No newline at end of file diff --git a/ai-provider/model-runtime/model-providers/cohere/cohere.yaml b/ai-provider/model-runtime/model-providers/cohere/cohere.yaml index fa6e0556..27419ba1 100644 --- a/ai-provider/model-runtime/model-providers/cohere/cohere.yaml +++ b/ai-provider/model-runtime/model-providers/cohere/cohere.yaml @@ -32,4 +32,12 @@ provider_credential_schema: zh_Hans: 在此输入您的 API Key en_US: Enter your API Key show_on: [ ] -address: https://api.cohere.com \ No newline at end of file + - variable: base_url + label: + en_US: https://api.cohere.ai/compatibility/v1 + type: text-input + required: false + placeholder: + zh_Hans: 在此输入您的 Base URL + en_US: Enter your Base URL +address: https://api.cohere.ai/compatibility/v1 \ No newline at end of file diff --git a/ai-provider/model-runtime/model-providers/deepseek/deepseek.yaml b/ai-provider/model-runtime/model-providers/deepseek/deepseek.yaml index 6e1bf082..1aebab42 100644 --- a/ai-provider/model-runtime/model-providers/deepseek/deepseek.yaml +++ b/ai-provider/model-runtime/model-providers/deepseek/deepseek.yaml @@ -30,4 +30,12 @@ provider_credential_schema: placeholder: zh_Hans: 在此输入您的 API Key en_US: Enter your API Key -address: https://api.deepseek.com \ No newline at end of file + - variable: base_url + label: + en_US: https://api.deepseek.com/v1 + type: text-input + required: false + placeholder: + zh_Hans: 在此输入您的 Base URL + en_US: Enter your Base URL +address: https://api.deepseek.com/v1 \ No newline at end of file diff --git a/ai-provider/model-runtime/model-providers/fireworks/fireworks.yaml b/ai-provider/model-runtime/model-providers/fireworks/fireworks.yaml index be734ac4..c375b6f3 100644 --- a/ai-provider/model-runtime/model-providers/fireworks/fireworks.yaml +++ b/ai-provider/model-runtime/model-providers/fireworks/fireworks.yaml @@ -28,4 +28,12 @@ provider_credential_schema: placeholder: zh_Hans: 在此输入您的 API Key en_US: Enter your API Key -address: https://api.fireworks.ai \ No newline at end of file + - variable: base_url + label: + en_US: https://generativelanguage.googleapis.com/v1beta/openai + type: text-input + required: false + placeholder: + zh_Hans: 在此输入您的 Base URL + en_US: Enter your Base URL +address: https://api.fireworks.ai/inference/v1 \ No newline at end of file diff --git a/ai-provider/model-runtime/model-providers/google/google.yaml b/ai-provider/model-runtime/model-providers/google/google.yaml index e54c899f..afc731cf 100644 --- a/ai-provider/model-runtime/model-providers/google/google.yaml +++ b/ai-provider/model-runtime/model-providers/google/google.yaml @@ -32,12 +32,12 @@ provider_credential_schema: - variable: google_api_base label: zh_Hans: API Base - en_US: API Base + en_US: https://generativelanguage.googleapis.com/v1beta/openai type: text-input required: false placeholder: zh_Hans: 在此输入您的 API Base, 如:https://api.google.com en_US: Enter your API Base, e.g. https://api.google.com -address: https://generativelanguage.googleapis.com +address: https://generativelanguage.googleapis.com/v1beta/openai recommend: true sort: 3 \ No newline at end of file diff --git a/ai-provider/model-runtime/model-providers/groq/groq.yaml b/ai-provider/model-runtime/model-providers/groq/groq.yaml index b2ce06f8..697a4e10 100644 --- a/ai-provider/model-runtime/model-providers/groq/groq.yaml +++ b/ai-provider/model-runtime/model-providers/groq/groq.yaml @@ -30,4 +30,12 @@ provider_credential_schema: placeholder: zh_Hans: 在此输入您的 API Key en_US: Enter your API Key -address: https://api.groq.com \ No newline at end of file + - variable: base_url + label: + en_US: https://api.groq.com/openai/v1 + type: text-input + required: false + placeholder: + zh_Hans: 在此输入您的 Base URL + en_US: Enter your Base URL +address: https://api.groq.com/openai/v1 \ No newline at end of file diff --git a/ai-provider/model-runtime/model-providers/huggingface_hub/huggingface_hub.yaml b/ai-provider/model-runtime/model-providers/huggingface_hub/huggingface_hub.yaml index e51e6081..8605c82c 100644 --- a/ai-provider/model-runtime/model-providers/huggingface_hub/huggingface_hub.yaml +++ b/ai-provider/model-runtime/model-providers/huggingface_hub/huggingface_hub.yaml @@ -100,4 +100,22 @@ model_credential_schema: show_on: - variable: huggingfacehub_api_type value: inference_endpoints -address: https://api-inference.huggingface.co \ No newline at end of file +provider_credential_schema: + credential_form_schemas: + - variable: api_key + label: + en_US: API Key + type: secret-input + required: true + placeholder: + zh_Hans: 在此输入您的 API Key + en_US: Enter your API Key + - variable: base_url + label: + en_US: https://router.huggingface.co/hf-inference/v1 + type: text-input + required: false + placeholder: + zh_Hans: 在此输入您的 Base URL + en_US: Enter your Base URL +address: https://router.huggingface.co/hf-inference/v1 \ No newline at end of file diff --git a/ai-provider/model-runtime/model-providers/hunyuan/hunyuan.yaml b/ai-provider/model-runtime/model-providers/hunyuan/hunyuan.yaml index 4b87e243..51a068dd 100644 --- a/ai-provider/model-runtime/model-providers/hunyuan/hunyuan.yaml +++ b/ai-provider/model-runtime/model-providers/hunyuan/hunyuan.yaml @@ -23,20 +23,36 @@ configurate_methods: - predefined-model provider_credential_schema: credential_form_schemas: - - variable: secret_id +# - variable: secret_id +# label: +# en_US: Secret ID +# type: secret-input +# required: true +# placeholder: +# zh_Hans: 在此输入您的 Secret ID +# en_US: Enter your Secret ID +# - variable: secret_key +# label: +# en_US: Secret Key +# type: secret-input +# required: true +# placeholder: +# zh_Hans: 在此输入您的 Secret Key +# en_US: Enter your Secret Key + - variable: api_key label: - en_US: Secret ID + en_US: API Key type: secret-input required: true placeholder: - zh_Hans: 在此输入您的 Secret ID - en_US: Enter your Secret ID - - variable: secret_key + zh_Hans: 在此输入您的 API Key + en_US: Enter your API Key + - variable: base_url label: - en_US: Secret Key - type: secret-input - required: true + en_US: https://api.hunyuan.cloud.tencent.com/v1 + type: text-input + required: false placeholder: - zh_Hans: 在此输入您的 Secret Key - en_US: Enter your Secret Key -address: https://hunyuan.tencentcloudapi.com \ No newline at end of file + zh_Hans: 在此输入您的 Base URL + en_US: Enter your Base URL +address: https://api.hunyuan.cloud.tencent.com/v1 \ No newline at end of file diff --git a/ai-provider/model-runtime/model-providers/lm_studio/assets/icon_l_en.svg b/ai-provider/model-runtime/model-providers/lm_studio/assets/icon_l_en.svg index f1ef8d4b..5f08476c 100644 --- a/ai-provider/model-runtime/model-providers/lm_studio/assets/icon_l_en.svg +++ b/ai-provider/model-runtime/model-providers/lm_studio/assets/icon_l_en.svg @@ -1,11 +1 @@ - - - - - - - - - - - +LM Studio \ No newline at end of file diff --git a/ai-provider/model-runtime/model-providers/lm_studio/assets/icon_s_en.svg b/ai-provider/model-runtime/model-providers/lm_studio/assets/icon_s_en.svg index 86f2c419..5f08476c 100644 --- a/ai-provider/model-runtime/model-providers/lm_studio/assets/icon_s_en.svg +++ b/ai-provider/model-runtime/model-providers/lm_studio/assets/icon_s_en.svg @@ -1,4 +1 @@ - - - - +LM Studio \ No newline at end of file diff --git a/ai-provider/model-runtime/model-providers/lm_studio/lm_studio.yaml b/ai-provider/model-runtime/model-providers/lm_studio/lm_studio.yaml index 5e62cd11..1b3306bc 100644 --- a/ai-provider/model-runtime/model-providers/lm_studio/lm_studio.yaml +++ b/ai-provider/model-runtime/model-providers/lm_studio/lm_studio.yaml @@ -96,4 +96,14 @@ model_credential_schema: label: en_US: 'No' zh_Hans: 否 +provider_credential_schema: + credential_form_schemas: + - variable: base_url + label: + en_US: https://lmstudio.ai + type: text-input + required: false + placeholder: + zh_Hans: 在此输入您的 Base URL + en_US: Enter your Base URL address: https://lmstudio.ai diff --git a/ai-provider/model-runtime/model-providers/minimax/minimax.yaml b/ai-provider/model-runtime/model-providers/minimax/minimax.yaml index d6d82e8a..981f2af5 100644 --- a/ai-provider/model-runtime/model-providers/minimax/minimax.yaml +++ b/ai-provider/model-runtime/model-providers/minimax/minimax.yaml @@ -27,6 +27,14 @@ provider_credential_schema: placeholder: zh_Hans: 在此输入您的 API Key en_US: Enter your API Key -address: https://api.minimax.chat + - variable: base_url + label: + en_US: https://api.minimax.chat/v1 + type: text-input + required: false + placeholder: + zh_Hans: 在此输入您的 Base URL + en_US: Enter your Base URL +address: https://api.minimax.chat/v1 recommend: true sort: 5 \ No newline at end of file diff --git a/ai-provider/model-runtime/model-providers/mistralai/mistralai.yaml b/ai-provider/model-runtime/model-providers/mistralai/mistralai.yaml index e00a4821..a9982657 100644 --- a/ai-provider/model-runtime/model-providers/mistralai/mistralai.yaml +++ b/ai-provider/model-runtime/model-providers/mistralai/mistralai.yaml @@ -29,4 +29,12 @@ provider_credential_schema: placeholder: zh_Hans: 在此输入您的 API Key en_US: Enter your API Key + - variable: base_url + label: + en_US: https://api.mistral.ai + type: text-input + required: false + placeholder: + zh_Hans: 在此输入您的 Base URL + en_US: Enter your Base URL address: https://api.mistral.ai \ No newline at end of file diff --git a/ai-provider/model-runtime/model-providers/moonshot/moonshot.yaml b/ai-provider/model-runtime/model-providers/moonshot/moonshot.yaml index 406213b3..58786544 100644 --- a/ai-provider/model-runtime/model-providers/moonshot/moonshot.yaml +++ b/ai-provider/model-runtime/model-providers/moonshot/moonshot.yaml @@ -31,6 +31,14 @@ provider_credential_schema: placeholder: zh_Hans: 在此输入您的 API Key en_US: Enter your API Key -address: https://api.moonshot.cn + - variable: base_url + label: + en_US: https://api.moonshot.cn/v1 + type: text-input + required: false + placeholder: + zh_Hans: 在此输入您的 Base URL + en_US: Enter your Base URL +address: https://api.moonshot.cn/v1 recommend: true sort: 6 \ No newline at end of file diff --git a/ai-provider/model-runtime/model-providers/novita/novita.yaml b/ai-provider/model-runtime/model-providers/novita/novita.yaml index 852c3422..fd126939 100644 --- a/ai-provider/model-runtime/model-providers/novita/novita.yaml +++ b/ai-provider/model-runtime/model-providers/novita/novita.yaml @@ -29,4 +29,12 @@ provider_credential_schema: placeholder: zh_Hans: 在此输入您的 API Key en_US: Enter your API Key -address: https://api.novita.ai \ No newline at end of file + - variable: base_url + label: + en_US: https://api.novita.ai/v3/openai + type: text-input + required: false + placeholder: + zh_Hans: 在此输入您的 Base URL + en_US: Enter your Base URL +address: https://api.novita.ai/v3/openai \ No newline at end of file diff --git a/ai-provider/model-runtime/model-providers/nvidia/nvidia.yaml b/ai-provider/model-runtime/model-providers/nvidia/nvidia.yaml index c2038512..821c4f94 100644 --- a/ai-provider/model-runtime/model-providers/nvidia/nvidia.yaml +++ b/ai-provider/model-runtime/model-providers/nvidia/nvidia.yaml @@ -31,4 +31,12 @@ provider_credential_schema: placeholder: zh_Hans: 在此输入您的 API Key en_US: Enter your API Key + - variable: base_url + label: + en_US: https://integrate.api.nvidia.com + type: text-input + required: false + placeholder: + zh_Hans: 在此输入您的 Base URL + en_US: Enter your Base URL address: https://integrate.api.nvidia.com \ No newline at end of file diff --git a/ai-provider/model-runtime/model-providers/ollama/assets/icon_l_en.svg b/ai-provider/model-runtime/model-providers/ollama/assets/icon_l_en.svg index 5f08476c..492903f4 100644 --- a/ai-provider/model-runtime/model-providers/ollama/assets/icon_l_en.svg +++ b/ai-provider/model-runtime/model-providers/ollama/assets/icon_l_en.svg @@ -1 +1,9 @@ -LM Studio \ No newline at end of file + + + + + + + + + diff --git a/ai-provider/model-runtime/model-providers/ollama/assets/icon_s_en.svg b/ai-provider/model-runtime/model-providers/ollama/assets/icon_s_en.svg index 5f08476c..492903f4 100644 --- a/ai-provider/model-runtime/model-providers/ollama/assets/icon_s_en.svg +++ b/ai-provider/model-runtime/model-providers/ollama/assets/icon_s_en.svg @@ -1 +1,9 @@ -LM Studio \ No newline at end of file + + + + + + + + + diff --git a/ai-provider/model-runtime/model-providers/ollama/ollama.yaml b/ai-provider/model-runtime/model-providers/ollama/ollama.yaml index b9144630..d662a3fd 100644 --- a/ai-provider/model-runtime/model-providers/ollama/ollama.yaml +++ b/ai-provider/model-runtime/model-providers/ollama/ollama.yaml @@ -17,6 +17,17 @@ supported_model_types: - text-embedding configurate_methods: - customizable-model +provider_credential_schema: + credential_form_schemas: + - variable: base_url + label: + zh_Hans: API Base + en_US: API Base + type: text-input + required: false + placeholder: + zh_Hans: 在此输入您的 API Base, 如:http://127.0.0.1:11434 + en_US: Enter your API Base, e.g. http://127.0.0.1:11434 model_credential_schema: model: label: diff --git a/ai-provider/model-runtime/model-providers/openAI/openai.yaml b/ai-provider/model-runtime/model-providers/openAI/openai.yaml index 2d5cfca6..75e1bbe9 100644 --- a/ai-provider/model-runtime/model-providers/openAI/openai.yaml +++ b/ai-provider/model-runtime/model-providers/openAI/openai.yaml @@ -78,6 +78,6 @@ provider_credential_schema: placeholder: zh_Hans: 在此输入您的 API Base, 如:https://api.openai.com en_US: Enter your API Base, e.g. https://api.openai.com -address: https://api.openai.com +address: https://api.openai.com/v1 recommend: true sort: 1 \ No newline at end of file diff --git a/ai-provider/model-runtime/model-providers/openrouter/openrouter.yaml b/ai-provider/model-runtime/model-providers/openrouter/openrouter.yaml index ad95d283..a1eedbac 100644 --- a/ai-provider/model-runtime/model-providers/openrouter/openrouter.yaml +++ b/ai-provider/model-runtime/model-providers/openrouter/openrouter.yaml @@ -103,4 +103,12 @@ provider_credential_schema: placeholder: zh_Hans: 在此输入您的 API Key en_US: Enter your API Key -address: https://openrouter.ai \ No newline at end of file + - variable: base_url + label: + en_US: https://openrouter.ai/api/v1 + type: text-input + required: false + placeholder: + zh_Hans: 在此输入您的 Base URL + en_US: Enter your Base URL +address: https://openrouter.ai/api/v1 \ No newline at end of file diff --git a/ai-provider/model-runtime/model-providers/perfxcloud/perfxcloud.yaml b/ai-provider/model-runtime/model-providers/perfxcloud/perfxcloud.yaml index bd63e495..4cc7a112 100644 --- a/ai-provider/model-runtime/model-providers/perfxcloud/perfxcloud.yaml +++ b/ai-provider/model-runtime/model-providers/perfxcloud/perfxcloud.yaml @@ -31,4 +31,12 @@ provider_credential_schema: placeholder: zh_Hans: 在此输入您的 API Key en_US: Enter your API Key -address: https://cloud.perfxlab.cn \ No newline at end of file + - variable: base_url + label: + en_US: https://perfxcloud.io/v1 + type: text-input + required: false + placeholder: + zh_Hans: 在此输入您的 Base URL + en_US: Enter your Base URL +address: https://perfxcloud.io/v1 \ No newline at end of file diff --git a/ai-provider/model-runtime/model-providers/siliconflow/siliconflow.yaml b/ai-provider/model-runtime/model-providers/siliconflow/siliconflow.yaml index f9c932f4..ab5cb9b4 100644 --- a/ai-provider/model-runtime/model-providers/siliconflow/siliconflow.yaml +++ b/ai-provider/model-runtime/model-providers/siliconflow/siliconflow.yaml @@ -30,4 +30,12 @@ provider_credential_schema: placeholder: zh_Hans: 在此输入您的 API Key en_US: Enter your API Key -address: https://api.openai.com \ No newline at end of file + - variable: base_url + label: + en_US: https://perfxcloud.io/v1 + type: text-input + required: false + placeholder: + zh_Hans: 在此输入您的 Base URL + en_US: Enter your Base URL +address: https://api.siliconflow.cn/v1 \ No newline at end of file diff --git a/ai-provider/model-runtime/model-providers/spark/spark.yaml b/ai-provider/model-runtime/model-providers/spark/spark.yaml index cbfedef4..cc8fcc8f 100644 --- a/ai-provider/model-runtime/model-providers/spark/spark.yaml +++ b/ai-provider/model-runtime/model-providers/spark/spark.yaml @@ -20,13 +20,20 @@ configurate_methods: - predefined-model provider_credential_schema: credential_form_schemas: - - variable: api_password + - variable: api_key label: - en_US: APIPassword + en_US: API Key type: secret-input required: true placeholder: - zh_Hans: 在此输入您的 APIPassword - en_US: Enter your APIPassword - -address: https://spark-api-open.xf-yun.com \ No newline at end of file + zh_Hans: 在此输入您的 API Key + en_US: Enter your API Key + - variable: base_url + label: + en_US: https://spark-api-open.xf-yun.com/v1 + type: text-input + required: false + placeholder: + zh_Hans: 在此输入您的 Base URL + en_US: Enter your Base URL +address: https://spark-api-open.xf-yun.com/v1 \ No newline at end of file diff --git a/ai-provider/model-runtime/model-providers/stepfun/stepfun.yaml b/ai-provider/model-runtime/model-providers/stepfun/stepfun.yaml index 5466ec4e..83434305 100644 --- a/ai-provider/model-runtime/model-providers/stepfun/stepfun.yaml +++ b/ai-provider/model-runtime/model-providers/stepfun/stepfun.yaml @@ -31,6 +31,14 @@ provider_credential_schema: placeholder: zh_Hans: 在此输入您的 API Key en_US: Enter your API Key + - variable: base_url + label: + en_US: https://api.stepfun.com/v1 + type: text-input + required: false + placeholder: + zh_Hans: 在此输入您的 Base URL + en_US: Enter your Base URL model_credential_schema: model: label: @@ -79,4 +87,4 @@ model_credential_schema: label: en_US: Tool Call zh_Hans: Tool Call -address: https://api.stepfun.com \ No newline at end of file +address: https://api.stepfun.com/v1 \ No newline at end of file diff --git a/ai-provider/model-runtime/model-providers/tongyi/tongyi.yaml b/ai-provider/model-runtime/model-providers/tongyi/tongyi.yaml index da535348..78c070e2 100644 --- a/ai-provider/model-runtime/model-providers/tongyi/tongyi.yaml +++ b/ai-provider/model-runtime/model-providers/tongyi/tongyi.yaml @@ -29,4 +29,12 @@ provider_credential_schema: placeholder: zh_Hans: 在此输入您的 API Key en_US: Enter your API Key -address: https://dashscope.aliyuncs.com \ No newline at end of file + - variable: dashscope_api_base + label: + en_US: https://dashscope.aliyuncs.com/compatible-mode/v1 + type: text-input + required: false + placeholder: + zh_Hans: 在此输入您的 Base URL + en_US: Enter your Base URL +address: https://dashscope.aliyuncs.com/compatible-mode/v1 \ No newline at end of file diff --git a/ai-provider/model-runtime/model-providers/upstage/upstage.yaml b/ai-provider/model-runtime/model-providers/upstage/upstage.yaml index b52bd1bb..ec44fc82 100644 --- a/ai-provider/model-runtime/model-providers/upstage/upstage.yaml +++ b/ai-provider/model-runtime/model-providers/upstage/upstage.yaml @@ -47,4 +47,12 @@ provider_credential_schema: placeholder: zh_Hans: 在此输入您的 API Key en_US: Enter your API Key -address: https://api.upstage.ai \ No newline at end of file + - variable: base_url + label: + en_US: https://api.upstage.ai/v1/solar + type: text-input + required: false + placeholder: + zh_Hans: 在此输入您的 Base URL + en_US: Enter your Base URL +address: https://api.upstage.ai/v1/solar \ No newline at end of file diff --git a/ai-provider/model-runtime/model-providers/vertex_ai/llm/anthropic.claude-3-haiku.yaml b/ai-provider/model-runtime/model-providers/vertex_ai/llm/anthropic.claude-3-haiku.yaml index 56133486..fb6f6d71 100644 --- a/ai-provider/model-runtime/model-providers/vertex_ai/llm/anthropic.claude-3-haiku.yaml +++ b/ai-provider/model-runtime/model-providers/vertex_ai/llm/anthropic.claude-3-haiku.yaml @@ -1,4 +1,4 @@ -model: claude-3-haiku@20240307 +model: google/claude-3-haiku@20240307 label: en_US: Claude 3 Haiku model_type: llm diff --git a/ai-provider/model-runtime/model-providers/vertex_ai/llm/anthropic.claude-3-opus.yaml b/ai-provider/model-runtime/model-providers/vertex_ai/llm/anthropic.claude-3-opus.yaml index ab084636..767199de 100644 --- a/ai-provider/model-runtime/model-providers/vertex_ai/llm/anthropic.claude-3-opus.yaml +++ b/ai-provider/model-runtime/model-providers/vertex_ai/llm/anthropic.claude-3-opus.yaml @@ -1,4 +1,4 @@ -model: claude-3-opus@20240229 +model: google/claude-3-opus@20240229 label: en_US: Claude 3 Opus model_type: llm diff --git a/ai-provider/model-runtime/model-providers/vertex_ai/llm/anthropic.claude-3-sonnet.yaml b/ai-provider/model-runtime/model-providers/vertex_ai/llm/anthropic.claude-3-sonnet.yaml index 0be0113f..a5f99d54 100644 --- a/ai-provider/model-runtime/model-providers/vertex_ai/llm/anthropic.claude-3-sonnet.yaml +++ b/ai-provider/model-runtime/model-providers/vertex_ai/llm/anthropic.claude-3-sonnet.yaml @@ -1,4 +1,4 @@ -model: claude-3-sonnet@20240229 +model: google/claude-3-sonnet@20240229 label: en_US: Claude 3 Sonnet model_type: llm diff --git a/ai-provider/model-runtime/model-providers/vertex_ai/llm/anthropic.claude-3.5-sonnet-v2.yaml b/ai-provider/model-runtime/model-providers/vertex_ai/llm/anthropic.claude-3.5-sonnet-v2.yaml index 37b9f30c..48a1a866 100644 --- a/ai-provider/model-runtime/model-providers/vertex_ai/llm/anthropic.claude-3.5-sonnet-v2.yaml +++ b/ai-provider/model-runtime/model-providers/vertex_ai/llm/anthropic.claude-3.5-sonnet-v2.yaml @@ -1,4 +1,4 @@ -model: claude-3-5-sonnet-v2@20241022 +model: google/claude-3-5-sonnet-v2@20241022 label: en_US: Claude 3.5 Sonnet v2 model_type: llm diff --git a/ai-provider/model-runtime/model-providers/vertex_ai/llm/anthropic.claude-3.5-sonnet.yaml b/ai-provider/model-runtime/model-providers/vertex_ai/llm/anthropic.claude-3.5-sonnet.yaml index c64384e6..0f17f26d 100644 --- a/ai-provider/model-runtime/model-providers/vertex_ai/llm/anthropic.claude-3.5-sonnet.yaml +++ b/ai-provider/model-runtime/model-providers/vertex_ai/llm/anthropic.claude-3.5-sonnet.yaml @@ -1,4 +1,4 @@ -model: claude-3-5-sonnet@20240620 +model: google/claude-3-5-sonnet@20240620 label: en_US: Claude 3.5 Sonnet model_type: llm diff --git a/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-1.0-pro-vision.yaml b/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-1.0-pro-vision.yaml index 4630f063..161903c3 100644 --- a/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-1.0-pro-vision.yaml +++ b/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-1.0-pro-vision.yaml @@ -1,4 +1,4 @@ -model: gemini-1.0-pro-vision-001 +model: google/gemini-1.0-pro-vision-001 label: en_US: Gemini 1.0 Pro Vision model_type: llm diff --git a/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-1.0-pro.yaml b/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-1.0-pro.yaml index 7a50d8c1..6fb6398e 100644 --- a/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-1.0-pro.yaml +++ b/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-1.0-pro.yaml @@ -1,4 +1,4 @@ -model: gemini-1.0-pro-002 +model: google/gemini-1.0-pro-002 label: en_US: Gemini 1.0 Pro model_type: llm diff --git a/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-1.5-flash-001.yaml b/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-1.5-flash-001.yaml index 1d9a0b0c..7862d4c0 100644 --- a/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-1.5-flash-001.yaml +++ b/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-1.5-flash-001.yaml @@ -1,4 +1,4 @@ -model: gemini-1.5-flash-001 +model: google/gemini-1.5-flash-001 label: en_US: Gemini 1.5 Flash 001 model_type: llm diff --git a/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-1.5-flash-002.yaml b/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-1.5-flash-002.yaml index 066d4ba3..85306da5 100644 --- a/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-1.5-flash-002.yaml +++ b/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-1.5-flash-002.yaml @@ -1,4 +1,4 @@ -model: gemini-1.5-flash-002 +model: google/gemini-1.5-flash-002 label: en_US: Gemini 1.5 Flash 002 model_type: llm diff --git a/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-1.5-pro-001.yaml b/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-1.5-pro-001.yaml index 33d2c25a..2aeb4ea8 100644 --- a/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-1.5-pro-001.yaml +++ b/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-1.5-pro-001.yaml @@ -1,4 +1,4 @@ -model: gemini-1.5-pro-001 +model: google/gemini-1.5-pro-001 label: en_US: Gemini 1.5 Pro 001 model_type: llm diff --git a/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-1.5-pro-002.yaml b/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-1.5-pro-002.yaml index 8b4c33cc..fb7acb5c 100644 --- a/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-1.5-pro-002.yaml +++ b/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-1.5-pro-002.yaml @@ -1,4 +1,4 @@ -model: gemini-1.5-pro-002 +model: google/gemini-1.5-pro-002 label: en_US: Gemini 1.5 Pro 002 model_type: llm diff --git a/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-2.0-flash-001.yaml b/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-2.0-flash-001.yaml index bef7ca5e..89bcc8e4 100644 --- a/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-2.0-flash-001.yaml +++ b/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-2.0-flash-001.yaml @@ -1,4 +1,4 @@ -model: gemini-2.0-flash-001 +model: google/gemini-2.0-flash-001 label: en_US: Gemini 2.0 Flash 001 model_type: llm diff --git a/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-2.0-flash-exp.yaml b/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-2.0-flash-exp.yaml index bcd59623..e8c7382b 100644 --- a/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-2.0-flash-exp.yaml +++ b/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-2.0-flash-exp.yaml @@ -1,4 +1,4 @@ -model: gemini-2.0-flash-exp +model: google/gemini-2.0-flash-exp label: en_US: Gemini 2.0 Flash Exp model_type: llm diff --git a/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-2.0-flash-lite-preview-02-05.yaml b/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-2.0-flash-lite-preview-02-05.yaml index 9c0a1e06..65e75f07 100644 --- a/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-2.0-flash-lite-preview-02-05.yaml +++ b/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-2.0-flash-lite-preview-02-05.yaml @@ -1,4 +1,4 @@ -model: gemini-2.0-flash-lite-preview-02-05 +model: google/gemini-2.0-flash-lite-preview-02-05 label: en_US: Gemini 2.0 Flash Lite Preview 0205 model_type: llm diff --git a/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-2.0-flash-thinking-exp-01-21.yaml b/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-2.0-flash-thinking-exp-01-21.yaml index 6e2fc767..4e384651 100644 --- a/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-2.0-flash-thinking-exp-01-21.yaml +++ b/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-2.0-flash-thinking-exp-01-21.yaml @@ -1,4 +1,4 @@ -model: gemini-2.0-flash-thinking-exp-01-21 +model: google/gemini-2.0-flash-thinking-exp-01-21 label: en_US: Gemini 2.0 Flash Thinking Exp 0121 model_type: llm diff --git a/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-2.0-pro-exp-02-05.yaml b/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-2.0-pro-exp-02-05.yaml index 96926a17..7166ec7b 100644 --- a/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-2.0-pro-exp-02-05.yaml +++ b/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-2.0-pro-exp-02-05.yaml @@ -1,4 +1,4 @@ -model: gemini-2.0-pro-exp-02-05 +model: google/gemini-2.0-pro-exp-02-05 label: en_US: Gemini 2.0 Pro Exp 0205 model_type: llm diff --git a/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-exp-1114.yaml b/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-exp-1114.yaml index bd49b476..e79bc321 100644 --- a/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-exp-1114.yaml +++ b/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-exp-1114.yaml @@ -1,4 +1,4 @@ -model: gemini-exp-1114 +model: google/gemini-exp-1114 label: en_US: Gemini exp 1114 model_type: llm diff --git a/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-exp-1121.yaml b/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-exp-1121.yaml index 8e3f218d..4c1d7de7 100644 --- a/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-exp-1121.yaml +++ b/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-exp-1121.yaml @@ -1,4 +1,4 @@ -model: gemini-exp-1121 +model: google/gemini-exp-1121 label: en_US: Gemini exp 1121 model_type: llm diff --git a/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-exp-1206.yaml b/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-exp-1206.yaml index 7a7c361c..268c34e9 100644 --- a/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-exp-1206.yaml +++ b/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-exp-1206.yaml @@ -1,4 +1,4 @@ -model: gemini-exp-1206 +model: google/gemini-exp-1206 label: en_US: Gemini exp 1206 model_type: llm diff --git a/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-flash-experimental.yaml b/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-flash-experimental.yaml index ef340372..c6573493 100644 --- a/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-flash-experimental.yaml +++ b/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-flash-experimental.yaml @@ -1,4 +1,4 @@ -model: gemini-flash-experimental +model: google/gemini-flash-experimental label: en_US: Gemini Flash Experimental model_type: llm diff --git a/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-pro-experimental.yaml b/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-pro-experimental.yaml index cff10423..67667fb5 100644 --- a/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-pro-experimental.yaml +++ b/ai-provider/model-runtime/model-providers/vertex_ai/llm/gemini-pro-experimental.yaml @@ -1,4 +1,4 @@ -model: gemini-pro-experimental +model: google/gemini-pro-experimental label: en_US: Gemini Pro Experimental model_type: llm diff --git a/ai-provider/model-runtime/model-providers/vertex_ai/vertex_ai.yaml b/ai-provider/model-runtime/model-providers/vertex_ai/vertex_ai.yaml index 43945e5f..1016af99 100644 --- a/ai-provider/model-runtime/model-providers/vertex_ai/vertex_ai.yaml +++ b/ai-provider/model-runtime/model-providers/vertex_ai/vertex_ai.yaml @@ -20,6 +20,15 @@ configurate_methods: - predefined-model provider_credential_schema: credential_form_schemas: + - variable: vertex_api_base + label: + zh_Hans: API Base + en_US: API Base + type: text-input + required: true + placeholder: + zh_Hans: 在此输入您的 API Base + en_US: Enter your API Base - variable: vertex_project_id label: en_US: Project ID @@ -36,9 +45,12 @@ provider_credential_schema: en_US: Enter your Google Cloud Location - variable: vertex_service_account_key label: - en_US: Service Account Key (Leave blank if you use Application Default Credentials) + en_US: Enter your Google Cloud Service Account Key in base64 format type: secret-input - required: false + required: true placeholder: en_US: Enter your Google Cloud Service Account Key in base64 format -address: https://api.openai.com \ No newline at end of file +address: https://api.openai.com +model_config: + access_configuration_status: true + access_configuration_demo: "{\"vertex_location\": \"\", \"vertex_project_id\": \"\", \"vertex_model\":\"\"}" \ No newline at end of file diff --git a/ai-provider/model-runtime/model-providers/volcengine_maas/volcengine_maas.yaml b/ai-provider/model-runtime/model-providers/volcengine_maas/volcengine_maas.yaml index 1a970c63..487f0c8d 100644 --- a/ai-provider/model-runtime/model-providers/volcengine_maas/volcengine_maas.yaml +++ b/ai-provider/model-runtime/model-providers/volcengine_maas/volcengine_maas.yaml @@ -1,342 +1,357 @@ -provider: volcengine_maas -label: - en_US: Volcengine -description: - en_US: Volcengine Ark models. - zh_Hans: 火山方舟提供的模型,例如 Doubao-pro-4k、Doubao-pro-32k 和 Doubao-pro-128k。 -icon_small: - en_US: icon_s_en.svg -icon_large: - en_US: icon_l_en.svg - zh_Hans: icon_l_zh.svg -background: "#F9FAFB" -help: - title: - en_US: Get your Access Key and Secret Access Key from Volcengine Console - zh_Hans: 从火山引擎控制台获取您的 Access Key 和 Secret Access Key - url: - en_US: https://console.volcengine.com/iam/keymanage/ -supported_model_types: - - llm - - text-embedding -configurate_methods: - - customizable-model -model_credential_schema: - model: - label: - en_US: Model Name - zh_Hans: 模型名称 - placeholder: - en_US: Enter your Model Name - zh_Hans: 输入模型名称 - credential_form_schemas: - - variable: auth_method - required: true - label: - en_US: Authentication Method - zh_Hans: 鉴权方式 - type: select - default: aksk - options: - - label: - en_US: API Key - value: api_key - - label: - en_US: Access Key / Secret Access Key - value: aksk - placeholder: - en_US: Enter your Authentication Method - zh_Hans: 选择鉴权方式 - - variable: volc_access_key_id - required: true - show_on: - - variable: auth_method - value: aksk - label: - en_US: Access Key - zh_Hans: Access Key - type: secret-input - placeholder: - en_US: Enter your Access Key - zh_Hans: 输入您的 Access Key - - variable: volc_secret_access_key - required: true - show_on: - - variable: auth_method - value: aksk - label: - en_US: Secret Access Key - zh_Hans: Secret Access Key - type: secret-input - placeholder: - en_US: Enter your Secret Access Key - zh_Hans: 输入您的 Secret Access Key - - variable: volc_api_key - required: true - show_on: - - variable: auth_method - value: api_key - label: - en_US: API Key - type: secret-input - placeholder: - en_US: Enter your API Key - zh_Hans: 输入您的 API Key - - variable: volc_region - required: true - label: - en_US: Volcengine Region - zh_Hans: 火山引擎地域 - type: text-input - default: cn-beijing - placeholder: - en_US: Enter Volcengine Region - zh_Hans: 输入火山引擎地域 - - variable: api_endpoint_host - required: true - label: - en_US: API Endpoint Host - zh_Hans: API Endpoint Host - type: text-input - default: https://ark.cn-beijing.volces.com/api/v3 - placeholder: - en_US: Enter your API Endpoint Host - zh_Hans: 输入 API Endpoint Host - - variable: endpoint_id - required: true - label: - en_US: Endpoint ID - zh_Hans: Endpoint ID - type: text-input - placeholder: - en_US: Enter your Endpoint ID - zh_Hans: 输入您的 Endpoint ID - - variable: base_model_name - label: - en_US: Base Model - zh_Hans: 基础模型 - type: select - required: true - options: - - label: - en_US: DeepSeek-R1-Distill-Qwen-32B - value: DeepSeek-R1-Distill-Qwen-32B - show_on: - - variable: __model_type - value: llm - - label: - en_US: DeepSeek-R1-Distill-Qwen-7B - value: DeepSeek-R1-Distill-Qwen-7B - show_on: - - variable: __model_type - value: llm - - label: - en_US: DeepSeek-R1 - value: DeepSeek-R1 - show_on: - - variable: __model_type - value: llm - - label: - en_US: DeepSeek-V3 - value: DeepSeek-V3 - show_on: - - variable: __model_type - value: llm - - label: - en_US: Doubao-1.5-vision-pro-32k - value: Doubao-1.5-vision-pro-32k - show_on: - - variable: __model_type - value: llm - - label: - en_US: Doubao-1.5-pro-32k - value: Doubao-1.5-pro-32k - show_on: - - variable: __model_type - value: llm - - label: - en_US: Doubao-1.5-lite-32k - value: Doubao-1.5-lite-32k - show_on: - - variable: __model_type - value: llm - - label: - en_US: Doubao-1.5-pro-256k - value: Doubao-1.5-pro-256k - show_on: - - variable: __model_type - value: llm - - label: - en_US: Doubao-vision-pro-32k - value: Doubao-vision-pro-32k - show_on: - - variable: __model_type - value: llm - - label: - en_US: Doubao-vision-lite-32k - value: Doubao-vision-lite-32k - show_on: - - variable: __model_type - value: llm - - label: - en_US: Doubao-pro-4k - value: Doubao-pro-4k - show_on: - - variable: __model_type - value: llm - - label: - en_US: Doubao-lite-4k - value: Doubao-lite-4k - show_on: - - variable: __model_type - value: llm - - label: - en_US: Doubao-pro-32k - value: Doubao-pro-32k - show_on: - - variable: __model_type - value: llm - - label: - en_US: Doubao-lite-32k - value: Doubao-lite-32k - show_on: - - variable: __model_type - value: llm - - label: - en_US: Doubao-pro-128k - value: Doubao-pro-128k - show_on: - - variable: __model_type - value: llm - - label: - en_US: Doubao-lite-128k - value: Doubao-lite-128k - show_on: - - variable: __model_type - value: llm - - label: - en_US: Doubao-pro-256k - value: Doubao-pro-256k - show_on: - - variable: __model_type - value: llm - - label: - en_US: Llama3-8B - value: Llama3-8B - show_on: - - variable: __model_type - value: llm - - label: - en_US: Llama3-70B - value: Llama3-70B - show_on: - - variable: __model_type - value: llm - - label: - en_US: Moonshot-v1-8k - value: Moonshot-v1-8k - show_on: - - variable: __model_type - value: llm - - label: - en_US: Moonshot-v1-32k - value: Moonshot-v1-32k - show_on: - - variable: __model_type - value: llm - - label: - en_US: Moonshot-v1-128k - value: Moonshot-v1-128k - show_on: - - variable: __model_type - value: llm - - label: - en_US: GLM3-130B - value: GLM3-130B - show_on: - - variable: __model_type - value: llm - - label: - en_US: GLM3-130B-Fin - value: GLM3-130B-Fin - show_on: - - variable: __model_type - value: llm - - label: - en_US: Mistral-7B - value: Mistral-7B - show_on: - - variable: __model_type - value: llm - - label: - en_US: Doubao-embedding - value: Doubao-embedding - show_on: - - variable: __model_type - value: text-embedding - - label: - en_US: Doubao-embedding-large - value: Doubao-embedding-large - show_on: - - variable: __model_type - value: text-embedding - - label: - en_US: Custom - zh_Hans: 自定义 - value: Custom - - variable: mode - required: true - show_on: - - variable: __model_type - value: llm - - variable: base_model_name - value: Custom - label: - zh_Hans: 模型类型 - en_US: Completion Mode - type: select - default: chat - placeholder: - zh_Hans: 选择对话类型 - en_US: Select Completion Mode - options: - - value: completion - label: - en_US: Completion - zh_Hans: 补全 - - value: chat - label: - en_US: Chat - zh_Hans: 对话 - - variable: context_size - required: true - show_on: - - variable: base_model_name - value: Custom - label: - zh_Hans: 模型上下文长度 - en_US: Model Context Size - type: text-input - default: "4096" - placeholder: - zh_Hans: 输入您的模型上下文长度 - en_US: Enter your Model Context Size - - variable: max_tokens - required: true - show_on: - - variable: __model_type - value: llm - - variable: base_model_name - value: Custom - label: - zh_Hans: 最大 token 上限 - en_US: Upper Bound for Max Tokens - default: "4096" - type: text-input - placeholder: - zh_Hans: 输入您的模型最大 token 上限 - en_US: Enter your model Upper Bound for Max Tokens -address: https://open.volcengine.com -model_config: - access_configuration_status: true - access_configuration_demo: "{\"endpoint\": \"https://196.1.1.2:3824\"}" \ No newline at end of file +provider: volcengine_maas +label: + en_US: Volcengine +description: + en_US: Volcengine Ark models. + zh_Hans: 火山方舟提供的模型,例如 Doubao-pro-4k、Doubao-pro-32k 和 Doubao-pro-128k。 +icon_small: + en_US: icon_s_en.svg +icon_large: + en_US: icon_l_en.svg + zh_Hans: icon_l_zh.svg +background: "#F9FAFB" +help: + title: + en_US: Get your Access Key and Secret Access Key from Volcengine Console + zh_Hans: 从火山引擎控制台获取您的 Access Key 和 Secret Access Key + url: + en_US: https://console.volcengine.com/iam/keymanage/ +supported_model_types: + - llm + - text-embedding +configurate_methods: + - customizable-model + +model_credential_schema: + model: + label: + en_US: Model Name + zh_Hans: 模型名称 + placeholder: + en_US: Enter your Model Name + zh_Hans: 输入模型名称 + credential_form_schemas: + - variable: auth_method + required: true + label: + en_US: Authentication Method + zh_Hans: 鉴权方式 + type: select + default: aksk + options: + - label: + en_US: API Key + value: api_key + - label: + en_US: Access Key / Secret Access Key + value: aksk + placeholder: + en_US: Enter your Authentication Method + zh_Hans: 选择鉴权方式 + - variable: volc_access_key_id + required: true + show_on: + - variable: auth_method + value: aksk + label: + en_US: Access Key + zh_Hans: Access Key + type: secret-input + placeholder: + en_US: Enter your Access Key + zh_Hans: 输入您的 Access Key + - variable: volc_secret_access_key + required: true + show_on: + - variable: auth_method + value: aksk + label: + en_US: Secret Access Key + zh_Hans: Secret Access Key + type: secret-input + placeholder: + en_US: Enter your Secret Access Key + zh_Hans: 输入您的 Secret Access Key + - variable: volc_api_key + required: true + show_on: + - variable: auth_method + value: api_key + label: + en_US: API Key + type: secret-input + placeholder: + en_US: Enter your API Key + zh_Hans: 输入您的 API Key + - variable: volc_region + required: true + label: + en_US: Volcengine Region + zh_Hans: 火山引擎地域 + type: text-input + default: cn-beijing + placeholder: + en_US: Enter Volcengine Region + zh_Hans: 输入火山引擎地域 + - variable: api_endpoint_host + required: true + label: + en_US: API Endpoint Host + zh_Hans: API Endpoint Host + type: text-input + default: https://ark.cn-beijing.volces.com/api/v3 + placeholder: + en_US: Enter your API Endpoint Host + zh_Hans: 输入 API Endpoint Host + - variable: endpoint_id + required: true + label: + en_US: Endpoint ID + zh_Hans: Endpoint ID + type: text-input + placeholder: + en_US: Enter your Endpoint ID + zh_Hans: 输入您的 Endpoint ID + - variable: base_model_name + label: + en_US: Base Model + zh_Hans: 基础模型 + type: select + required: true + options: + - label: + en_US: DeepSeek-R1-Distill-Qwen-32B + value: DeepSeek-R1-Distill-Qwen-32B + show_on: + - variable: __model_type + value: llm + - label: + en_US: DeepSeek-R1-Distill-Qwen-7B + value: DeepSeek-R1-Distill-Qwen-7B + show_on: + - variable: __model_type + value: llm + - label: + en_US: DeepSeek-R1 + value: DeepSeek-R1 + show_on: + - variable: __model_type + value: llm + - label: + en_US: DeepSeek-V3 + value: DeepSeek-V3 + show_on: + - variable: __model_type + value: llm + - label: + en_US: Doubao-1.5-vision-pro-32k + value: Doubao-1.5-vision-pro-32k + show_on: + - variable: __model_type + value: llm + - label: + en_US: Doubao-1.5-pro-32k + value: Doubao-1.5-pro-32k + show_on: + - variable: __model_type + value: llm + - label: + en_US: Doubao-1.5-lite-32k + value: Doubao-1.5-lite-32k + show_on: + - variable: __model_type + value: llm + - label: + en_US: Doubao-1.5-pro-256k + value: Doubao-1.5-pro-256k + show_on: + - variable: __model_type + value: llm + - label: + en_US: Doubao-vision-pro-32k + value: Doubao-vision-pro-32k + show_on: + - variable: __model_type + value: llm + - label: + en_US: Doubao-vision-lite-32k + value: Doubao-vision-lite-32k + show_on: + - variable: __model_type + value: llm + - label: + en_US: Doubao-pro-4k + value: Doubao-pro-4k + show_on: + - variable: __model_type + value: llm + - label: + en_US: Doubao-lite-4k + value: Doubao-lite-4k + show_on: + - variable: __model_type + value: llm + - label: + en_US: Doubao-pro-32k + value: Doubao-pro-32k + show_on: + - variable: __model_type + value: llm + - label: + en_US: Doubao-lite-32k + value: Doubao-lite-32k + show_on: + - variable: __model_type + value: llm + - label: + en_US: Doubao-pro-128k + value: Doubao-pro-128k + show_on: + - variable: __model_type + value: llm + - label: + en_US: Doubao-lite-128k + value: Doubao-lite-128k + show_on: + - variable: __model_type + value: llm + - label: + en_US: Doubao-pro-256k + value: Doubao-pro-256k + show_on: + - variable: __model_type + value: llm + - label: + en_US: Llama3-8B + value: Llama3-8B + show_on: + - variable: __model_type + value: llm + - label: + en_US: Llama3-70B + value: Llama3-70B + show_on: + - variable: __model_type + value: llm + - label: + en_US: Moonshot-v1-8k + value: Moonshot-v1-8k + show_on: + - variable: __model_type + value: llm + - label: + en_US: Moonshot-v1-32k + value: Moonshot-v1-32k + show_on: + - variable: __model_type + value: llm + - label: + en_US: Moonshot-v1-128k + value: Moonshot-v1-128k + show_on: + - variable: __model_type + value: llm + - label: + en_US: GLM3-130B + value: GLM3-130B + show_on: + - variable: __model_type + value: llm + - label: + en_US: GLM3-130B-Fin + value: GLM3-130B-Fin + show_on: + - variable: __model_type + value: llm + - label: + en_US: Mistral-7B + value: Mistral-7B + show_on: + - variable: __model_type + value: llm + - label: + en_US: Doubao-embedding + value: Doubao-embedding + show_on: + - variable: __model_type + value: text-embedding + - label: + en_US: Doubao-embedding-large + value: Doubao-embedding-large + show_on: + - variable: __model_type + value: text-embedding + - label: + en_US: Custom + zh_Hans: 自定义 + value: Custom + - variable: mode + required: true + show_on: + - variable: __model_type + value: llm + - variable: base_model_name + value: Custom + label: + zh_Hans: 模型类型 + en_US: Completion Mode + type: select + default: chat + placeholder: + zh_Hans: 选择对话类型 + en_US: Select Completion Mode + options: + - value: completion + label: + en_US: Completion + zh_Hans: 补全 + - value: chat + label: + en_US: Chat + zh_Hans: 对话 + - variable: context_size + required: true + show_on: + - variable: base_model_name + value: Custom + label: + zh_Hans: 模型上下文长度 + en_US: Model Context Size + type: text-input + default: "4096" + placeholder: + zh_Hans: 输入您的模型上下文长度 + en_US: Enter your Model Context Size + - variable: max_tokens + required: true + show_on: + - variable: __model_type + value: llm + - variable: base_model_name + value: Custom + label: + zh_Hans: 最大 token 上限 + en_US: Upper Bound for Max Tokens + default: "4096" + type: text-input + placeholder: + zh_Hans: 输入您的模型最大 token 上限 + en_US: Enter your model Upper Bound for Max Tokens +provider_credential_schema: + credential_form_schemas: + - variable: api_key + label: + en_US: APIKey + type: secret-input + required: true + placeholder: + zh_Hans: 在此输入您的 APIKey + en_US: Enter your APIKey + - variable: base_url + label: + en_US: https://ark.cn-beijing.volces.com/api/v3 + required: true + placeholder: + zh_Hans: 在此输入您的 Base url + en_US: Enter your Base url +address: https://ark.cn-beijing.volces.com/api/v3 diff --git a/ai-provider/model-runtime/model-providers/wenxin/wenxin.yaml b/ai-provider/model-runtime/model-providers/wenxin/wenxin.yaml index e429ea11..603ed9a5 100644 --- a/ai-provider/model-runtime/model-providers/wenxin/wenxin.yaml +++ b/ai-provider/model-runtime/model-providers/wenxin/wenxin.yaml @@ -30,14 +30,14 @@ provider_credential_schema: placeholder: zh_Hans: 在此输入您的 API Key en_US: Enter your API Key - - variable: secret_key + - variable: base_url label: - en_US: Secret Key - type: secret-input - required: true + en_US: https://qianfan.baidubce.com/v2 + type: text-input + required: false placeholder: - zh_Hans: 在此输入您的 Secret Key - en_US: Enter your Secret Key -address: https://aip.baidubce.com + zh_Hans: 在此输入您的 Base URL + en_US: Enter your Base URL +address: https://qianfan.baidubce.com/v2 recommend: true sort: 7 \ No newline at end of file diff --git a/ai-provider/model-runtime/model-providers/xinference/xinference.yaml b/ai-provider/model-runtime/model-providers/xinference/xinference.yaml index 6d284644..21192037 100644 --- a/ai-provider/model-runtime/model-providers/xinference/xinference.yaml +++ b/ai-provider/model-runtime/model-providers/xinference/xinference.yaml @@ -76,4 +76,14 @@ model_credential_schema: placeholder: zh_Hans: 在此输入调用重试次数 en_US: Enter max retries +provider_credential_schema: + credential_form_schemas: + - variable: base_url + label: + en_US: Base URL + type: text-input + required: false + placeholder: + zh_Hans: 在此输入您的 Base URL + en_US: Enter your Base URL address: https://xinference.ai \ No newline at end of file diff --git a/ai-provider/model-runtime/model-providers/yi/yi.yaml b/ai-provider/model-runtime/model-providers/yi/yi.yaml index 919cba1b..5d9156f6 100644 --- a/ai-provider/model-runtime/model-providers/yi/yi.yaml +++ b/ai-provider/model-runtime/model-providers/yi/yi.yaml @@ -30,4 +30,12 @@ provider_credential_schema: placeholder: zh_Hans: 在此输入您的 API Key en_US: Enter your API Key -address: https://api.lingyiwanwu.com \ No newline at end of file + - variable: base_url + label: + en_US: https://api.lingyiwanwu.com/v1 + type: text-input + required: false + placeholder: + zh_Hans: 在此输入您的 Base URL + en_US: Enter your Base URL +address: https://api.lingyiwanwu.com/v1 \ No newline at end of file diff --git a/ai-provider/model-runtime/model-providers/zhinao/zhinao.yaml b/ai-provider/model-runtime/model-providers/zhinao/zhinao.yaml index 7d2c5372..ff62cd36 100644 --- a/ai-provider/model-runtime/model-providers/zhinao/zhinao.yaml +++ b/ai-provider/model-runtime/model-providers/zhinao/zhinao.yaml @@ -30,4 +30,12 @@ provider_credential_schema: placeholder: zh_Hans: 在此输入您的 API Key en_US: Enter your API Key + - variable: base_url + label: + en_US: Base URL + type: text-input + required: false + placeholder: + zh_Hans: 在此输入您的 Base URL + en_US: Enter your Base URL address: https://api.360.cn \ No newline at end of file diff --git a/ai-provider/model-runtime/model-providers/zhipuai/zhipuai.yaml b/ai-provider/model-runtime/model-providers/zhipuai/zhipuai.yaml index 3a33ee45..c318c8b8 100644 --- a/ai-provider/model-runtime/model-providers/zhipuai/zhipuai.yaml +++ b/ai-provider/model-runtime/model-providers/zhipuai/zhipuai.yaml @@ -29,4 +29,11 @@ provider_credential_schema: placeholder: zh_Hans: 在此输入您的 APIKey en_US: Enter your APIKey -address: https://open.bigmodel.cn \ No newline at end of file + - variable: base_url + label: + en_US: https://open.bigmodel.cn/api/paas/v4/ + required: true + placeholder: + zh_Hans: 在此输入您的 Base url + en_US: Enter your Base url +address: https://open.bigmodel.cn/api/paas/v4/ \ No newline at end of file diff --git a/ai-provider/model-runtime/model.go b/ai-provider/model-runtime/model.go index 44d29e2f..2768f2a7 100644 --- a/ai-provider/model-runtime/model.go +++ b/ai-provider/model-runtime/model.go @@ -97,6 +97,10 @@ func NewModel(data string, logo string) (IModel, error) { params := make(ParamValidator, 0, len(cfg.ParameterRules)) defaultConfig := make(map[string]interface{}) for _, p := range cfg.ParameterRules { + if (p.Default == nil || p.Default == "" || p.Default == 0 || p.Default == false) && !p.Required { + defaultConfig[p.Name] = nil + continue + } t := p.Type if t == "" { t = ParameterTypeStr diff --git a/ai-provider/model-runtime/provider.go b/ai-provider/model-runtime/provider.go index 435f44fd..5183810f 100644 --- a/ai-provider/model-runtime/provider.go +++ b/ai-provider/model-runtime/provider.go @@ -50,7 +50,7 @@ type IProviderInfo interface { } func GetCustomizeLogo() string { - logo, _ := providerDir.ReadFile("customize/assets/icon_s_en.svg") + logo, _ := providerDir.ReadFile("model-providers/customize/assets/icon_s_en.svg") return string(logo) } diff --git a/app/ai-event-handler/nsq.go b/app/ai-event-handler/nsq.go index 3f891490..be133e6e 100644 --- a/app/ai-event-handler/nsq.go +++ b/app/ai-event-handler/nsq.go @@ -81,7 +81,7 @@ func convertInt(value interface{}) int { func genAIKey(key string, provider string) string { keys := strings.Split(key, "@") - return strings.TrimSuffix(keys[0], fmt.Sprintf("-%s", provider)) + return strings.TrimPrefix(keys[0], fmt.Sprintf("%s-", provider)) } // HandleMessage 处理从 NSQ 读取的消息 diff --git a/common/regexp.go b/common/regexp.go index 3e78ec44..2c1486ed 100644 --- a/common/regexp.go +++ b/common/regexp.go @@ -25,6 +25,8 @@ const ( CIDRIpv4Exp = `^(?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([1-9]|[1-2]\d|3[0-2]))?$` // CheckPathIPPortExp (scheme://)?ip:port CheckPathIPPortExp = `([a-zA-z]+://)?((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}:[0-9]+` + // EnglishOrNumberOrSpecialChars a-zA-Z0-9-_.: + EnglishOrNumberOrSpecialChars = `^(?i)[-/:._a-z0-9]+$` ) var ( @@ -47,6 +49,8 @@ var ( restfulPathMatchRegexp = regexp.MustCompile(`({[0-9a-zA-Z-_]+})+`) //restfulParamMatchRegexp 匹配restful参数 {xxx} restfulParamMatchRegexp = regexp.MustCompile(`^{[0-9a-zA-Z-_]+}$`) + // modelNameRegexp match model name + modelNameRegexp = regexp.MustCompile(EnglishOrNumberOrSpecialChars) ) func IsMatchString(regexpPattern RegexpPattern, s string) error { @@ -129,3 +133,8 @@ func ReplaceRestfulPath(path, replaceStr string) string { func CheckPathContainsIPPort(path string) bool { return checkIPPortRegexp.MatchString(path) } + +// ModelNameValid check model name is valid +func ModelNameValid(param string) bool { + return modelNameRegexp.MatchString(param) +} diff --git a/controller/ai-api/iml.go b/controller/ai-api/iml.go index 05e9baca..83292cbe 100644 --- a/controller/ai-api/iml.go +++ b/controller/ai-api/iml.go @@ -4,6 +4,8 @@ import ( "context" "net/http" + ai_provider_local "github.com/APIParkLab/APIPark/ai-provider/local" + "github.com/APIParkLab/APIPark/model/plugin_model" ai_api "github.com/APIParkLab/APIPark/module/ai-api" ai_api_dto "github.com/APIParkLab/APIPark/module/ai-api/dto" @@ -48,13 +50,13 @@ func (i *imlAPIController) Create(ctx *gin.Context, serviceId string, input *ai_ } } if input.AiModel != nil { - provider := "ollama" + provider := ai_provider_local.ProviderLocal if input.AiModel.Type != "local" { provider = input.AiModel.Provider } plugins["ai_formatter"] = api.PluginSetting{ Config: plugin_model.ConfigType{ - "model": input.AiModel.Id, + "model": input.AiModel.Name, "provider": provider, "config": input.AiModel.Config, }, @@ -63,6 +65,7 @@ func (i *imlAPIController) Create(ctx *gin.Context, serviceId string, input *ai_ _, err = i.routerModule.Create(ctx, serviceId, &router_dto.Create{ Id: input.Id, + Name: input.Name, Path: input.Path, Methods: []string{ http.MethodPost, @@ -106,13 +109,13 @@ func (i *imlAPIController) Edit(ctx *gin.Context, serviceId string, apiId string } //var upstream *string if input.AiModel != nil { - provider := "ollama" + provider := ai_provider_local.ProviderLocal if input.AiModel.Type != "local" { provider = input.AiModel.Provider } proxy.Plugins["ai_formatter"] = api.PluginSetting{ Config: plugin_model.ConfigType{ - "model": input.AiModel.Id, + "model": input.AiModel.Name, "provider": provider, "config": input.AiModel.Config, }, @@ -130,6 +133,7 @@ func (i *imlAPIController) Edit(ctx *gin.Context, serviceId string, apiId string } _, err = i.routerModule.Edit(ctx, serviceId, apiId, &router_dto.Edit{ + Name: input.Name, Description: input.Description, Proxy: proxy, Path: input.Path, diff --git a/controller/ai-local/iml.go b/controller/ai-local/iml.go index e436609a..a34d4a9b 100644 --- a/controller/ai-local/iml.go +++ b/controller/ai-local/iml.go @@ -245,7 +245,7 @@ func (i *imlLocalModelController) initAILocalService(ctx context.Context, model } serviceId := uuid.NewString() prefix := fmt.Sprintf("/%s", serviceId[:8]) - providerId := "ollama" + providerId := ai_provider_local.ProviderLocal err = i.transaction.Transaction(ctx, func(ctx context.Context) error { _, err = i.serviceModule.Create(ctx, teamID, &service_dto.CreateService{ Id: serviceId, @@ -267,7 +267,7 @@ func (i *imlLocalModelController) initAILocalService(ctx context.Context, model }) return func() error { - path := fmt.Sprintf("/%s/chat", strings.Trim(prefix, "/")) + path := fmt.Sprintf("/%s/chat/completions", strings.Trim(prefix, "/")) timeout := 300000 retry := 0 aiPrompt := &ai_api_dto.AiPrompt{ @@ -276,7 +276,7 @@ func (i *imlLocalModelController) initAILocalService(ctx context.Context, model } aiModel := &ai_api_dto.AiModel{ Id: model, - Config: ai_provider_local.OllamaConfig, + Config: ai_provider_local.LocalConfig, Provider: providerId, Type: "local", } diff --git a/controller/ai-model/iml.go b/controller/ai-model/iml.go index fcb48134..21f62e39 100644 --- a/controller/ai-model/iml.go +++ b/controller/ai-model/iml.go @@ -3,6 +3,7 @@ package ai_model import ( "encoding/json" "fmt" + "github.com/APIParkLab/APIPark/common" ai_model "github.com/APIParkLab/APIPark/module/ai-model" model_dto "github.com/APIParkLab/APIPark/module/ai-model/dto" "github.com/gin-gonic/gin" @@ -22,8 +23,8 @@ func (i *imlProviderModelController) GetModelParametersTemplate(ctx *gin.Context } func (i *imlProviderModelController) UpdateProviderModel(ctx *gin.Context, provider string, input *model_dto.EditModel) error { - if strings.TrimSpace(input.Name) == "" { - return fmt.Errorf("name is empty") + if !common.ModelNameValid(input.Name) { + return fmt.Errorf("model name is invalid(a-zA-Z0-9-_.:/)") } if strings.TrimSpace(input.Id) == "" { return fmt.Errorf("id is empty") @@ -54,8 +55,8 @@ func (i *imlProviderModelController) DeleteProviderModel(ctx *gin.Context, provi } func (i *imlProviderModelController) AddProviderModel(ctx *gin.Context, provider string, input *model_dto.Model) (*model_dto.SimpleModel, error) { - if strings.TrimSpace(input.Name) == "" { - return nil, fmt.Errorf("name is empty") + if !common.ModelNameValid(input.Name) { + return nil, fmt.Errorf("model name illegal(a-zA-Z0-9-_.:/)") } if strings.TrimSpace(provider) == "" { return nil, fmt.Errorf("provider is empty") diff --git a/controller/ai/iml.go b/controller/ai/iml.go index ae37239b..9b1c4737 100644 --- a/controller/ai/iml.go +++ b/controller/ai/iml.go @@ -3,6 +3,7 @@ package ai import ( "encoding/json" "fmt" + "github.com/APIParkLab/APIPark/common" "strconv" "strings" @@ -24,8 +25,8 @@ func (i *imlProviderController) Delete(ctx *gin.Context, id string) error { } func (i *imlProviderController) AddProvider(ctx *gin.Context, input *ai_dto.NewProvider) (*ai_dto.SimpleProvider, error) { - if strings.TrimSpace(input.Name) == "" { - return nil, fmt.Errorf("name is empty") + if !common.ModelNameValid(input.Name) { + return nil, fmt.Errorf("name illegal(a-zA-Z0-9-_.:/)") } return i.module.AddProvider(ctx, input) } diff --git a/controller/mcp/iml.go b/controller/mcp/iml.go new file mode 100644 index 00000000..6e7c6a4b --- /dev/null +++ b/controller/mcp/iml.go @@ -0,0 +1,141 @@ +package mcp + +import ( + "fmt" + "net/http" + "strings" + "sync" + + mcp_server "github.com/APIParkLab/APIPark/mcp-server" + "github.com/APIParkLab/APIPark/module/mcp" + "github.com/APIParkLab/APIPark/module/system" + "github.com/eolinker/go-common/utils" + "github.com/gin-gonic/gin" + mcp2 "github.com/mark3labs/mcp-go/mcp" + "github.com/mark3labs/mcp-go/server" +) + +var _ IMcpController = (*imlMcpController)(nil) + +type imlMcpController struct { + settingModule system.ISettingModule `autowired:""` + mcpModule mcp.IMcpModule `autowired:""` + sessionKeys sync.Map + server http.Handler + openServer http.Handler +} + +var mcpDefaultConfig = `{ + "mcpServers": { + "%s": { + "url": "%s" + } + } +} +` + +func (i *imlMcpController) GlobalMCPConfig(ctx *gin.Context) (string, error) { + cfg := i.settingModule.Get(ctx) + if cfg.SitePrefix == "" { + return "", fmt.Errorf("site prefix is empty") + } + return fmt.Sprintf(mcpDefaultConfig, "APIPark-MCP-Server", fmt.Sprintf("%s/openapi/v1/%s/sse?apikey={your_api_key}", strings.TrimSuffix(cfg.SitePrefix, "/"), mcp_server.GlobalBasePath)), nil +} + +func (i *imlMcpController) OnComplete() { + s := server.NewMCPServer("APIPark MCP Server", "1.0.0", server.WithLogging()) + s.AddTool( + mcp2.NewTool( + "apipark_service_list", + mcp2.WithDescription("This tool is a standardized interface provided by the Apipark platform under the MCP (Model Context Protocol) framework, designed to retrieve metadata for all registered services in bulk. By invoking this tool, users can efficiently explore the complete list of published services and their core attributes, serving as a prerequisite for subsequent actions such as querying detailed API lists via service IDs, requesting access permissions, or integrating services."), + mcp2.WithString("keyword", mcp2.Description("Keyword for fuzzy search")), + ), + i.mcpModule.Services, + ) + s.AddTool( + mcp2.NewTool( + "apipark_service_api_list", + mcp2.WithDescription("This tool is a standardized MCP (Model Context Protocol) interface provided by the Apipark platform, designed to retrieve OpenAPI specification documents for all APIs under a specified service using its service ID. By invoking this tool, users gain precise access to detailed API definitions (including endpoints, parameters, request/response schemas) for debugging, integration, or client SDK generation."), + mcp2.WithString("service", mcp2.Description("Service ID")), + ), + i.mcpModule.APIs, + ) + s.AddTool( + mcp2.NewTool( + "apipark_invoke_api", + mcp2.WithDescription("This tool is a core MCP (Model Context Protocol) interface provided by the Apipark platform, enabling users to programmatically invoke APIs using metadata from apipark_service_api_list (API schemas). It acts as a unified gateway for executing API requests with built-in authentication, parameter validation, and error handling, returning structured responses for integration workflows."), + mcp2.WithString("path", mcp2.Description("API path"), mcp2.Required()), + mcp2.WithString("method", mcp2.Description("API method"), mcp2.Required()), + mcp2.WithString("content-type", mcp2.Description("API Request Content-Type. If method is POST,PUT,PATCH, it must be set. If not set, it will be ignored.")), + mcp2.WithObject("query", mcp2.Description("API Request query,param type is map[string]string")), + mcp2.WithObject("header", mcp2.Description("API Request header,param type is map[string]string")), + mcp2.WithString("body", mcp2.Description("API Request body")), + ), + i.mcpModule.Invoke, + ) + i.server = server.NewSSEServer(s, server.WithBasePath(fmt.Sprintf("/api/v1/%s", mcp_server.GlobalBasePath))) + i.openServer = server.NewSSEServer(s, server.WithBasePath(fmt.Sprintf("/openapi/v1/%s", strings.Trim(mcp_server.GlobalBasePath, "/")))) +} + +func (i *imlMcpController) GlobalMCPHandle(ctx *gin.Context) { + cfg := i.settingModule.Get(ctx) + req := ctx.Request.WithContext(utils.SetGatewayInvoke(ctx.Request.Context(), cfg.InvokeAddress)) + + i.server.ServeHTTP(ctx.Writer, req) +} + +func (i *imlMcpController) GlobalHandleSSE(ctx *gin.Context) { + i.handleSSE(ctx, i.openServer) +} + +func (i *imlMcpController) handleSSE(ctx *gin.Context, server http.Handler) { + apikey := ctx.Request.URL.Query().Get("apikey") + writer := &ResponseWriter{ + Writer: ctx.Writer, + sessionId: make(chan string), + } + defer close(writer.sessionId) + sessionId := "" + go func() { + var ok bool + sessionId, ok = <-writer.sessionId + if !ok { + return + } + i.sessionKeys.Store(sessionId, apikey) + }() + server.ServeHTTP(writer, ctx.Request) + i.sessionKeys.Delete(sessionId) +} + +func (i *imlMcpController) GlobalHandleMessage(ctx *gin.Context) { + i.handleMessage(ctx, i.openServer) +} + +func (i *imlMcpController) MCPHandle(ctx *gin.Context) { + cfg := i.settingModule.Get(ctx) + + req := ctx.Request.WithContext(utils.SetGatewayInvoke(ctx.Request.Context(), cfg.InvokeAddress)) + mcp_server.ServeHTTP(ctx.Writer, req) +} + +func (i *imlMcpController) ServiceHandleSSE(ctx *gin.Context) { + i.handleSSE(ctx, mcp_server.DefaultMCPServer()) +} + +func (i *imlMcpController) ServiceHandleMessage(ctx *gin.Context) { + i.handleMessage(ctx, mcp_server.DefaultMCPServer()) +} + +func (i *imlMcpController) handleMessage(ctx *gin.Context, server http.Handler) { + sessionId := ctx.Request.URL.Query().Get("sessionId") + apikey, ok := i.sessionKeys.Load(sessionId) + if !ok { + ctx.String(403, "sessionId not found") + return + } + cfg := i.settingModule.Get(ctx) + req := ctx.Request.WithContext(utils.SetGatewayInvoke(ctx.Request.Context(), cfg.InvokeAddress)) + req = req.WithContext(utils.SetLabel(req.Context(), "apikey", apikey.(string))) + server.ServeHTTP(ctx.Writer, req) +} diff --git a/controller/mcp/mcp.go b/controller/mcp/mcp.go new file mode 100644 index 00000000..780a0aa9 --- /dev/null +++ b/controller/mcp/mcp.go @@ -0,0 +1,24 @@ +package mcp + +import ( + "reflect" + + "github.com/eolinker/go-common/autowire" + "github.com/gin-gonic/gin" +) + +type IMcpController interface { + MCPHandle(ctx *gin.Context) + GlobalMCPHandle(ctx *gin.Context) + GlobalHandleSSE(ctx *gin.Context) + GlobalHandleMessage(ctx *gin.Context) + ServiceHandleSSE(ctx *gin.Context) + ServiceHandleMessage(ctx *gin.Context) + GlobalMCPConfig(ctx *gin.Context) (string, error) +} + +func init() { + autowire.Auto[IMcpController](func() reflect.Value { + return reflect.ValueOf(new(imlMcpController)) + }) +} diff --git a/controller/mcp/writer.go b/controller/mcp/writer.go new file mode 100644 index 00000000..d2a48df0 --- /dev/null +++ b/controller/mcp/writer.go @@ -0,0 +1,35 @@ +package mcp + +import ( + "net/http" + "regexp" +) + +type ResponseWriter struct { + Writer http.ResponseWriter + sessionId chan string +} + +func (r *ResponseWriter) Flush() { + fluster, ok := r.Writer.(http.Flusher) + if ok { + fluster.Flush() + } +} + +func (r *ResponseWriter) Header() http.Header { + return r.Writer.Header() +} + +func (r *ResponseWriter) Write(bytes []byte) (int, error) { + re := regexp.MustCompile(`sessionId=([^&?\s]+)`) + match := re.FindStringSubmatch(string(bytes)) + if len(match) > 1 { + r.sessionId <- match[1] + } + return r.Writer.Write(bytes) +} + +func (r *ResponseWriter) WriteHeader(statusCode int) { + r.Writer.WriteHeader(statusCode) +} diff --git a/controller/service/iml.go b/controller/service/iml.go index e2f0bf32..489d66e2 100644 --- a/controller/service/iml.go +++ b/controller/service/iml.go @@ -296,7 +296,7 @@ func (i *imlServiceController) editAIService(ctx *gin.Context, id string, input if input.Provider == nil { return nil, fmt.Errorf("provider is required") } - if *input.Provider != "ollama" { + if *input.Provider != ai_provider_local.ProviderLocal { _, has := model_runtime.GetProvider(*input.Provider) if !has { return nil, fmt.Errorf("provider not found") @@ -330,7 +330,7 @@ func (i *imlServiceController) createAIService(ctx *gin.Context, teamID string, modelId := "" modelCfg := "" modelType := "online" - if *input.Provider == "ollama" { + if *input.Provider == ai_provider_local.ProviderLocal { modelType = "local" list, err := i.aiLocalModel.SimpleList(ctx) if err != nil { @@ -340,7 +340,7 @@ func (i *imlServiceController) createAIService(ctx *gin.Context, teamID string, return nil, fmt.Errorf("no local model") } modelId = list[0].Id - modelCfg = ai_provider_local.OllamaConfig + modelCfg = ai_provider_local.LocalConfig } else { pv, err := i.providerModule.Provider(ctx, *input.Provider) if err != nil { @@ -354,7 +354,8 @@ func (i *imlServiceController) createAIService(ctx *gin.Context, teamID string, if !has { return nil, fmt.Errorf("model %s not found", pv.DefaultLLM) } - modelId = m.ID() + //modelId = m.ID() + modelId = m.Name() modelCfg = m.DefaultConfig() } @@ -367,7 +368,7 @@ func (i *imlServiceController) createAIService(ctx *gin.Context, teamID string, return err } prefix := strings.Replace(input.Prefix, ":", "_", -1) - path := fmt.Sprintf("/%s/chat", strings.Trim(prefix, "/")) + path := fmt.Sprintf("/%s/chat/completions", strings.Trim(prefix, "/")) timeout := 300000 retry := 0 aiPrompt := &ai_api_dto.AiPrompt{ diff --git a/controller/system-apikey/apikey.go b/controller/system-apikey/apikey.go new file mode 100644 index 00000000..c59fc03e --- /dev/null +++ b/controller/system-apikey/apikey.go @@ -0,0 +1,25 @@ +package system_apikey + +import ( + "reflect" + + system_apikey_dto "github.com/APIParkLab/APIPark/module/system-apikey/dto" + "github.com/eolinker/go-common/autowire" + "github.com/gin-gonic/gin" +) + +type IAPIKeyController interface { + Create(ctx *gin.Context, input *system_apikey_dto.Create) error + Update(ctx *gin.Context, id string, input *system_apikey_dto.Update) error + Delete(ctx *gin.Context, id string) error + Get(ctx *gin.Context, id string) (*system_apikey_dto.APIKey, error) + Search(ctx *gin.Context, keyword string) ([]*system_apikey_dto.Item, error) + SimpleList(ctx *gin.Context) ([]*system_apikey_dto.SimpleItem, error) + MyAPIKeys(ctx *gin.Context) ([]*system_apikey_dto.SimpleItem, error) +} + +func init() { + autowire.Auto[IAPIKeyController](func() reflect.Value { + return reflect.ValueOf(new(imlAPIKeyController)) + }) +} diff --git a/controller/system-apikey/iml.go b/controller/system-apikey/iml.go new file mode 100644 index 00000000..7afdd451 --- /dev/null +++ b/controller/system-apikey/iml.go @@ -0,0 +1,41 @@ +package system_apikey + +import ( + system_apikey "github.com/APIParkLab/APIPark/module/system-apikey" + system_apikey_dto "github.com/APIParkLab/APIPark/module/system-apikey/dto" + "github.com/gin-gonic/gin" +) + +var _ IAPIKeyController = new(imlAPIKeyController) + +type imlAPIKeyController struct { + apikeyModule system_apikey.IAPIKeyModule `autowired:""` +} + +func (i *imlAPIKeyController) MyAPIKeys(ctx *gin.Context) ([]*system_apikey_dto.SimpleItem, error) { + return i.apikeyModule.MyAPIKeys(ctx) +} + +func (i *imlAPIKeyController) Create(ctx *gin.Context, input *system_apikey_dto.Create) error { + return i.apikeyModule.Create(ctx, input) +} + +func (i *imlAPIKeyController) Update(ctx *gin.Context, id string, input *system_apikey_dto.Update) error { + return i.apikeyModule.Update(ctx, id, input) +} + +func (i *imlAPIKeyController) Delete(ctx *gin.Context, id string) error { + return i.apikeyModule.Delete(ctx, id) +} + +func (i *imlAPIKeyController) Get(ctx *gin.Context, id string) (*system_apikey_dto.APIKey, error) { + return i.apikeyModule.Get(ctx, id) +} + +func (i *imlAPIKeyController) Search(ctx *gin.Context, keyword string) ([]*system_apikey_dto.Item, error) { + return i.apikeyModule.Search(ctx, keyword) +} + +func (i *imlAPIKeyController) SimpleList(ctx *gin.Context) ([]*system_apikey_dto.SimpleItem, error) { + return i.apikeyModule.SimpleList(ctx) +} diff --git a/controller/system/iml.go b/controller/system/iml.go index 44f9e37e..749e73b0 100644 --- a/controller/system/iml.go +++ b/controller/system/iml.go @@ -395,36 +395,36 @@ func (i *imlInitController) createAIService(ctx context.Context, teamID string, if err != nil { return err } - path := fmt.Sprintf("/%s/demo_translation_api", strings.Trim(input.Prefix, "/")) + path := fmt.Sprintf("/%s/chat/completions", strings.Trim(input.Prefix, "/")) timeout := 300000 retry := 0 aiPrompt := &ai_api_dto.AiPrompt{ - Variables: []*ai_api_dto.AiPromptVariable{ - { - Key: "source_lang", - Description: "", - Require: true, - }, - { - Key: "target_lang", - Description: "", - Require: true, - }, - { - Key: "text", - Description: "", - Require: true, - }, - }, - Prompt: "You need to translate {{source_lang}} into {{target_lang}}, and the following is the content that needs to be translated.\n---\n{{text}}", + //Variables: []*ai_api_dto.AiPromptVariable{ + // { + // Key: "source_lang", + // Description: "", + // Require: true, + // }, + // { + // Key: "target_lang", + // Description: "", + // Require: true, + // }, + // { + // Key: "text", + // Description: "", + // Require: true, + // }, + //}, + //Prompt: "You need to translate {{source_lang}} into {{target_lang}}, and the following is the content that needs to be translated.\n---\n{{text}}", } aiModel := &ai_api_dto.AiModel{ Id: m.ID(), Config: m.DefaultConfig(), Provider: providerId, } - name := "Demo Translation API" - description := "A demo that shows you how to use a prompt to create a Translation API." + name := "Demo Chat API" + description := "A demo that shows you how to use a prompt to create a Chat API." apiId := uuid.New().String() err = i.aiAPIModule.Create( ctx, diff --git a/frontend/packages/common/src/components/aoplatform/PublishApprovalModalContent.tsx b/frontend/packages/common/src/components/aoplatform/PublishApprovalModalContent.tsx index 6272fd42..5016fac9 100644 --- a/frontend/packages/common/src/components/aoplatform/PublishApprovalModalContent.tsx +++ b/frontend/packages/common/src/components/aoplatform/PublishApprovalModalContent.tsx @@ -1,5 +1,5 @@ import { App, Col, Form, Input, Row, Table, Tooltip } from 'antd' -import { forwardRef, useEffect, useImperativeHandle, useMemo } from 'react' +import { forwardRef, useEffect, useImperativeHandle, useMemo, useRef } from 'react' import { PublishApprovalInfoType, PublishApprovalModalHandle, @@ -36,6 +36,7 @@ export const PublishApprovalModalContent = forwardRef(null) const save: (operate: 'pass' | 'refuse') => Promise = (operate) => { if (type === 'view') { @@ -140,6 +141,12 @@ export const PublishApprovalModalContent = forwardRef { form.setFieldsValue({ opinion: '', ...data }) + // 如果是添加模式且insidePage为true,自动聚焦版本号输入框 + if (type === 'add' && insidePage && versionInputRef.current) { + setTimeout(() => { + versionInputRef.current?.focus() + }, 100) + } }, []) const translatedUpstreamColumns = useMemo( @@ -335,7 +342,12 @@ export const PublishApprovalModalContent = forwardRef - + diff --git a/frontend/packages/core/src/components/AIProviderSelect/index.tsx b/frontend/packages/core/src/components/AIProviderSelect/index.tsx index 5b9e28f4..c0a7fecd 100644 --- a/frontend/packages/core/src/components/AIProviderSelect/index.tsx +++ b/frontend/packages/core/src/components/AIProviderSelect/index.tsx @@ -91,8 +91,15 @@ const AIProviderSelect: React.FC = ({ value, onChange, so label: ( {provider.name} diff --git a/frontend/packages/core/src/const/ai-service/const.tsx b/frontend/packages/core/src/const/ai-service/const.tsx index ccf72b07..a058f636 100644 --- a/frontend/packages/core/src/const/ai-service/const.tsx +++ b/frontend/packages/core/src/const/ai-service/const.tsx @@ -19,11 +19,11 @@ export const AI_SERVICE_ROUTER_TABLE_COLUMNS: PageProColumns (
- {entity.model.id} + {entity.model.name}
) }, diff --git a/frontend/packages/core/src/const/ai-service/type.ts b/frontend/packages/core/src/const/ai-service/type.ts index d7086490..0c3e2841 100644 --- a/frontend/packages/core/src/const/ai-service/type.ts +++ b/frontend/packages/core/src/const/ai-service/type.ts @@ -104,6 +104,7 @@ export type AiServiceRouterTableListItem = { model:{ id:string logo:string + name:string } }; diff --git a/frontend/packages/core/src/index.css b/frontend/packages/core/src/index.css index d0f2228c..2f4ae252 100644 --- a/frontend/packages/core/src/index.css +++ b/frontend/packages/core/src/index.css @@ -666,6 +666,15 @@ p{ display: none; } +.apipark-layout-base-menu-horizontal-menu-item { + padding-right: 0px !important; + padding-left: 0px !important; +} +.apipark-layout-base-menu-horizontal-item-title { + padding-left: 16px; + padding-right: 16px; +} + .ant-pro-table-list-toolbar-setting-items{ position:absolute; top:18px; diff --git a/frontend/packages/core/src/pages/aiService/api/AiServiceInsideRouterCreate.tsx b/frontend/packages/core/src/pages/aiService/api/AiServiceInsideRouterCreate.tsx index 72eebb2d..bace0c54 100644 --- a/frontend/packages/core/src/pages/aiService/api/AiServiceInsideRouterCreate.tsx +++ b/frontend/packages/core/src/pages/aiService/api/AiServiceInsideRouterCreate.tsx @@ -80,7 +80,7 @@ const AiServiceInsideRouterCreate = () => { timeout, retry, aiPrompt: { variables: variables, prompt: prompt }, - aiModel: { id: defaultLlm?.id, provider: defaultLlm?.provider, config: defaultLlm?.config, type: defaultLlm?.type }, + aiModel: { id: defaultLlm?.id, provider: defaultLlm?.provider, config: defaultLlm?.config, type: defaultLlm?.type, name: defaultLlm?.name }, disabled } return fetchData>('service/ai-router', { @@ -162,7 +162,8 @@ const AiServiceInsideRouterCreate = () => { provider: aiModel?.provider, id: aiModel?.id, config: aiModel.config, - type: aiModel?.type + type: aiModel?.type, + name: aiModel?.name }) as AiProviderDefaultConfig & { config: string } ) getDefaultModelConfig({ @@ -216,8 +217,8 @@ const AiServiceInsideRouterCreate = () => { provider: localId, name: aiServiceInfo?.name, config: llmSetting?.defaultConfig || '', + ...(llmSetting ?? {}), type: 'local', - ...(llmSetting ?? {}) } as AiProviderDefaultConfig & { config: string } }) } @@ -229,7 +230,8 @@ const AiServiceInsideRouterCreate = () => { return { ...prev, logo: llmSetting?.logo, - scopes: llmSetting?.scopes + scopes: llmSetting?.scopes, + name: llmSetting?.name } as AiProviderDefaultConfig & { config: string } }) } @@ -259,8 +261,8 @@ const AiServiceInsideRouterCreate = () => { provider: data.provider.id, name: data.provider.name, config: llmSetting?.config || '', + ...(llmSetting ?? {}), type: 'online', - ...(llmSetting ?? {}) } as AiProviderDefaultConfig & { config: string } }) } @@ -272,7 +274,8 @@ const AiServiceInsideRouterCreate = () => { return { ...prev, logo: llmSetting?.logo, - scopes: llmSetting?.scopes + scopes: llmSetting?.scopes, + name: llmSetting?.name } as AiProviderDefaultConfig & { config: string } }) } @@ -385,7 +388,7 @@ const AiServiceInsideRouterCreate = () => { className="flex items-center h-[24px] ai-setting-svg-container " dangerouslySetInnerHTML={{ __html: defaultLlm?.logo || '' }} > - {defaultLlm?.id || defaultLlm?.defaultLlm} + {defaultLlm?.name || defaultLlm?.id || defaultLlm?.defaultLlm} {defaultLlm?.scopes?.map((x) => {x?.toLocaleUpperCase()})} diff --git a/frontend/packages/core/src/pages/aiService/api/AiServiceInsideRouterModelConfig.tsx b/frontend/packages/core/src/pages/aiService/api/AiServiceInsideRouterModelConfig.tsx index 3705a89e..70bce5e3 100644 --- a/frontend/packages/core/src/pages/aiService/api/AiServiceInsideRouterModelConfig.tsx +++ b/frontend/packages/core/src/pages/aiService/api/AiServiceInsideRouterModelConfig.tsx @@ -10,7 +10,7 @@ import { DefaultOptionType } from 'antd/es/select' import { forwardRef, useEffect, useImperativeHandle, useState } from 'react' export type AiServiceRouterModelConfigHandle = { - save: () => Promise<{ id: string; config: string, type: string, provider: string }> + save: () => Promise<{ id: string; config: string; type: string; provider: string }> } export type AiServiceRouterModelConfigProps = { @@ -49,7 +49,7 @@ const AiServiceRouterModelConfig = forwardRef { fetchData('simple/ai/models/local/configured', { @@ -113,12 +113,11 @@ const AiServiceRouterModelConfig = forwardRef { - return { ...x, label: x.name, value: x.id } - }) + data.providers?.map((x: SimpleAiProviderItem) => { + return { ...x, label: x.name, value: x.id } + }) ) - if (setDefaultValue && data.providers.length) { + if (setDefaultValue && data.providers.length) { const id = data.providers[0].id form.setFieldValue('provider', id) getLlmList(id) @@ -179,9 +178,14 @@ const AiServiceRouterModelConfig = forwardRef ({ - value: x.id, - label: ( -
- {x.name || x.id} - {modelType === 'online' && x?.scopes?.map((s: any) => {s?.toLocaleUpperCase()})} -
- ) - })) - } + filterOption={(input, option) => (option?.searchText ?? '').includes(input.toLowerCase())} + options={llmList?.map((x) => ({ + value: x.id, + label: ( +
+ {x.name || x.id} + {modelType === 'online' && x?.scopes?.map((s: any) => {s?.toLocaleUpperCase()})} +
+ ), + searchText: x.name.toLowerCase() + }))} onChange={(e) => { form.setFieldValue('config', llmList.find((x) => x.id === e)?.config) }} diff --git a/frontend/packages/core/src/pages/aiSetting/AiSettingModal.tsx b/frontend/packages/core/src/pages/aiSetting/AiSettingModal.tsx index 3cd3a0b7..fa53f3a0 100644 --- a/frontend/packages/core/src/pages/aiSetting/AiSettingModal.tsx +++ b/frontend/packages/core/src/pages/aiSetting/AiSettingModal.tsx @@ -283,8 +283,9 @@ const AiSettingModalContent = forwardRef { return addModelModalRef.current?.save().then((res) => { - if (res === true) { + if (res) { getLlmList(lastLlmID) + form.setFieldValue('defaultLlm', res) } }) }, diff --git a/frontend/packages/core/src/pages/aiSetting/OnlineModelList.tsx b/frontend/packages/core/src/pages/aiSetting/OnlineModelList.tsx index 2aee5fc7..6bad73db 100644 --- a/frontend/packages/core/src/pages/aiSetting/OnlineModelList.tsx +++ b/frontend/packages/core/src/pages/aiSetting/OnlineModelList.tsx @@ -87,7 +87,7 @@ const OnlineModelList: React.FC = () => { keyword: searchWord, page: params.current }, - eoTransformKeys: ['default_llm', 'api_count', 'key_count', 'model_count', 'can_delete'] + eoTransformKeys: ['default_llm', 'default_llm_name', 'api_count', 'key_count', 'model_count', 'can_delete'] }) if (response.code === STATUS_CODE.SUCCESS) { @@ -195,7 +195,7 @@ const OnlineModelList: React.FC = () => { { title: $t('默认模型'), ellipsis: true, - dataIndex: 'defaultLlm' + dataIndex: 'defaultLlmName' }, { title: $t('Models'), diff --git a/frontend/packages/core/src/pages/aiSetting/contexts/AddModels.tsx b/frontend/packages/core/src/pages/aiSetting/contexts/AddModels.tsx index 572ec51f..12c31f59 100644 --- a/frontend/packages/core/src/pages/aiSetting/contexts/AddModels.tsx +++ b/frontend/packages/core/src/pages/aiSetting/contexts/AddModels.tsx @@ -102,7 +102,7 @@ const AddModels = forwardRef((prop ...value, id: modelID } - fetchData>('ai/provider/model', { + fetchData>('ai/provider/model', { method: type === 'edit' ? 'PUT' : 'POST', eoParams: { provider: providerID }, eoBody: finalValue, @@ -112,7 +112,8 @@ const AddModels = forwardRef((prop const { code, msg } = response if (code === STATUS_CODE.SUCCESS) { message.success($t(RESPONSE_TIPS.success) || msg) - resolve(true) + const llmId = response.data?.model?.id + resolve(llmId) } else { message.error(msg || $t(RESPONSE_TIPS.error)) reject(msg || $t(RESPONSE_TIPS.error)) diff --git a/frontend/packages/core/src/pages/aiSetting/contexts/ModelsDetailTable.tsx b/frontend/packages/core/src/pages/aiSetting/contexts/ModelsDetailTable.tsx index 231e4e6d..fd658a54 100644 --- a/frontend/packages/core/src/pages/aiSetting/contexts/ModelsDetailTable.tsx +++ b/frontend/packages/core/src/pages/aiSetting/contexts/ModelsDetailTable.tsx @@ -57,11 +57,13 @@ const ModelsDetailTable = (props: { providerID?: string }) => { { title: $t('模型名称'), ellipsis: true, + copyable: true, dataIndex: 'name' }, { title: $t('模型类型'), ellipsis: true, + width: 100, dataIndex: 'type' }, { @@ -78,6 +80,7 @@ const ModelsDetailTable = (props: { providerID?: string }) => { ), ellipsis: true, + copyable: true, dataIndex: 'modelValue' }, ...operation @@ -153,7 +156,7 @@ const ModelsDetailTable = (props: { providerID?: string }) => { const tableData = response.data.llms.map((item) => { return { ...item, - modelValue: `${response.data.provider.name}/${item.name}` + modelValue: `${response.data.provider.id}/${item.name}` } }) return { diff --git a/frontend/packages/core/src/pages/keySettings/components/ApiKeyContent.tsx b/frontend/packages/core/src/pages/keySettings/components/ApiKeyContent.tsx index a303ce36..29338962 100644 --- a/frontend/packages/core/src/pages/keySettings/components/ApiKeyContent.tsx +++ b/frontend/packages/core/src/pages/keySettings/components/ApiKeyContent.tsx @@ -26,7 +26,7 @@ const ApiKeyContent: React.FC = forwardRef(({ provider, enti form.setFieldsValue({ name: entity.name, expire_time: isNeverExpire ? undefined : dayjs(entity.expire_time * 1000), - config: entity.config + config: entity.config ? JSON.stringify(JSON.parse(entity.config), null, 2) : '' }) } catch (e) { form.setFieldsValue({ diff --git a/frontend/packages/core/src/pages/loadBalancing/AddModel.tsx b/frontend/packages/core/src/pages/loadBalancing/AddModel.tsx index 1843d78f..cb94bea4 100644 --- a/frontend/packages/core/src/pages/loadBalancing/AddModel.tsx +++ b/frontend/packages/core/src/pages/loadBalancing/AddModel.tsx @@ -182,7 +182,9 @@ const AddLoadBalancingModel = forwardRef((props, ref: any) {modelType === 'online' && ( label={$t('模型供应商')} name="provider" rules={[{ required: true }]}> (option?.searchText ?? '').includes(input.toLowerCase())} loading={llmListLoading} options={ llmList?.map((x) => ({ @@ -212,7 +217,8 @@ const AddLoadBalancingModel = forwardRef((props, ref: any) {x.name || x.id} { modelType === 'online' &&x?.scopes?.map((s: any) => {s?.toLocaleUpperCase()})} - ) + ), + searchText: x.name.toLowerCase() })) } onChange={(value) => { diff --git a/frontend/packages/core/src/pages/logsettings/LogSettings.tsx b/frontend/packages/core/src/pages/logsettings/LogSettings.tsx index cf710367..124ee8d3 100644 --- a/frontend/packages/core/src/pages/logsettings/LogSettings.tsx +++ b/frontend/packages/core/src/pages/logsettings/LogSettings.tsx @@ -40,7 +40,7 @@ const LogSettings = () => { const menuData = useMemo(() => { const newMenu = menuItems?.map((x: DynamicMenuItem) => { return getItem( - {$t(x.title)}, + {$t(x.title)}, x.name, undefined, undefined, diff --git a/frontend/packages/core/src/pages/system/SystemConfig.tsx b/frontend/packages/core/src/pages/system/SystemConfig.tsx index 7062c530..f0c93662 100644 --- a/frontend/packages/core/src/pages/system/SystemConfig.tsx +++ b/frontend/packages/core/src/pages/system/SystemConfig.tsx @@ -138,7 +138,7 @@ const SystemConfig = forwardRef((_, ref) => { const { code, data, msg } = response if (code === STATUS_CODE.SUCCESS) { const localModelList = data.llms?.map((x: any) => { - return { ...x, label: x.id, value: x.id } + return { ...x, label: x.name || x.id, value: x.id } }) setModelList(localModelList) if (setDefaultLlm && localModelList.length > 0) { @@ -450,9 +450,14 @@ const SystemConfig = forwardRef((_, ref) => { > {providerOptionList && providerOptionList.length > 0 ? ( +
)} diff --git a/gateway/apinto/client.go b/gateway/apinto/client.go index 4c531891..775b40f1 100644 --- a/gateway/apinto/client.go +++ b/gateway/apinto/client.go @@ -14,6 +14,10 @@ type ClientDriver struct { client admin_client.Client } +func (c *ClientDriver) Hash() gateway.IHashClient { + return NewHashClient(c.client) +} + func (c *ClientDriver) Strategy() gateway.IStrategyClient { return NewStrategyClient(c.client) } diff --git a/gateway/apinto/hash.go b/gateway/apinto/hash.go new file mode 100644 index 00000000..7c0cd7dc --- /dev/null +++ b/gateway/apinto/hash.go @@ -0,0 +1,59 @@ +package apinto + +import ( + "context" + + "github.com/APIParkLab/APIPark/gateway" + admin_client "github.com/eolinker/eosc/process-admin/client" +) + +var _ gateway.IHashClient = &HashClient{} + +type HashClient struct { + client admin_client.Client +} + +func (s *HashClient) Online(ctx context.Context, resources ...*gateway.HashRelease) error { + s.client.Begin(ctx) + for _, r := range resources { + // 先删除所有,再set + err := s.client.HDelAll(ctx, r.HashKey) + if err != nil { + s.client.Rollback(ctx) + return err + } + for key, value := range r.HashMap { + err := s.client.HSet(ctx, r.HashKey, key, value) + if err != nil { + s.client.Rollback(ctx) + return err + } + } + } + return s.client.Commit(ctx) +} + +func (s *HashClient) Offline(ctx context.Context, resources ...*gateway.HashRelease) error { + s.client.Begin(ctx) + for _, r := range resources { + if len(r.HashMap) == 0 { + err := s.client.HDelAll(ctx, r.HashKey) + if err != nil { + s.client.Rollback(ctx) + return err + } + } + for key, _ := range r.HashMap { + err := s.client.HDel(ctx, r.HashKey, key) + if err != nil { + s.client.Rollback(ctx) + return err + } + } + } + return s.client.Commit(ctx) +} + +func NewHashClient(client admin_client.Client) *HashClient { + return &HashClient{client: client} +} diff --git a/gateway/client.go b/gateway/client.go index 293ffa2b..48b134e1 100644 --- a/gateway/client.go +++ b/gateway/client.go @@ -23,6 +23,7 @@ type IClientDriver interface { Subscribe() ISubscribeClient Strategy() IStrategyClient Dynamic(resource string) (IDynamicClient, error) + Hash() IHashClient PluginSetting() IPluginSetting Commit(ctx context.Context) error Rollback(ctx context.Context) error diff --git a/gateway/hash.go b/gateway/hash.go new file mode 100644 index 00000000..d13ec4bc --- /dev/null +++ b/gateway/hash.go @@ -0,0 +1,3 @@ +package gateway + +const KeyServiceMapping = "service_mapping" diff --git a/gateway/profession.go b/gateway/profession.go index 16956421..0be26331 100644 --- a/gateway/profession.go +++ b/gateway/profession.go @@ -70,6 +70,10 @@ var dynamicResourceMap = map[string]Worker{ Profession: ProfessionAIResource, Driver: "ai-key", }, + "ai-model": { + Profession: ProfessionAIResource, + Driver: "ai-model", + }, } type Worker struct { diff --git a/gateway/resource.go b/gateway/resource.go index 85931ead..d568a509 100644 --- a/gateway/resource.go +++ b/gateway/resource.go @@ -15,6 +15,8 @@ type IApplicationClient IResourceClient[ApplicationRelease] type IServiceClient IResourceClient[ServiceRelease] +type IHashClient IResourceClient[HashRelease] + type ISubscribeClient IResourceClient[SubscribeRelease] type IStrategyClient IResourceClient[eosc.Base[StrategyRelease]] @@ -133,6 +135,11 @@ type ServiceRelease struct { Apis []string } +type HashRelease struct { + HashKey string + HashMap map[string]string +} + type SubscribeRelease struct { // 订阅服务ID Service string diff --git a/go.mod b/go.mod index a347fe38..c54a7894 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ toolchain go1.23.6 require ( github.com/eolinker/ap-account v1.0.15 github.com/eolinker/eosc v0.18.3 - github.com/eolinker/go-common v1.1.5 + github.com/eolinker/go-common v1.1.6 github.com/gabriel-vasile/mimetype v1.4.4 github.com/getkin/kin-openapi v0.127.0 github.com/gin-contrib/gzip v1.0.1 @@ -15,9 +15,10 @@ require ( github.com/go-sql-driver/mysql v1.7.0 github.com/google/uuid v1.6.0 github.com/influxdata/influxdb-client-go/v2 v2.14.0 + github.com/mark3labs/mcp-go v0.17.0 + github.com/mitchellh/mapstructure v1.5.0 github.com/nsqio/go-nsq v1.1.0 github.com/ollama/ollama v0.5.8 - github.com/stretchr/testify v1.9.0 github.com/urfave/cli v1.22.16 golang.org/x/crypto v0.31.0 gopkg.in/yaml.v3 v3.0.1 @@ -32,7 +33,6 @@ require ( github.com/cloudwego/base64x v0.1.4 // indirect github.com/cloudwego/iasm v0.2.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dustin/go-humanize v1.0.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect @@ -61,11 +61,11 @@ require ( github.com/oapi-codegen/runtime v1.0.0 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect github.com/redis/go-redis/v9 v9.5.3 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect + github.com/yosida95/uritemplate/v3 v3.0.2 // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.13 // indirect go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect diff --git a/go.sum b/go.sum index 8e7dcdc0..7cf5d0f2 100644 --- a/go.sum +++ b/go.sum @@ -32,14 +32,15 @@ github.com/eolinker/ap-account v1.0.15 h1:n6DJeL6RHZ8eLlZUcY2U3H4d/GPaA5oelAx3R0 github.com/eolinker/ap-account v1.0.15/go.mod h1:zm/Ivs6waJ/M/nEszhpPmM6g50y/MKO+5eABFAdeD0g= github.com/eolinker/eosc v0.18.3 h1:3IK5HkAPnJRfLbQ0FR7kWsZr6Y/OiqqGazvN1q2BL5A= github.com/eolinker/eosc v0.18.3/go.mod h1:O9PQQXFCpB6fjHf+oFt/LN6EOAv779ItbMixMKCfTfk= -github.com/eolinker/go-common v1.1.5 h1:unYPcpptqL2zk+BNuzc4cJVJBDEpjs918nTkGUw+q7U= -github.com/eolinker/go-common v1.1.5/go.mod h1:Kb/jENMN1mApnodvRgV4YwO9FJby1Jkt2EUjrBjvSX4= +github.com/eolinker/go-common v1.1.6 h1:s+NaQL0InjX/MwWY53+8y8qzAgsULIUc4U6nWXWQ2Nw= +github.com/eolinker/go-common v1.1.6/go.mod h1:Kb/jENMN1mApnodvRgV4YwO9FJby1Jkt2EUjrBjvSX4= github.com/gabriel-vasile/mimetype v1.4.4 h1:QjV6pZ7/XZ7ryI2KuyeEDE8wnh7fHP9YnQy+R0LnH8I= github.com/gabriel-vasile/mimetype v1.4.4/go.mod h1:JwLei5XPtWdGiMFB5Pjle1oEeoSeEuJfJE+TtfvdB/s= github.com/getkin/kin-openapi v0.127.0 h1:Mghqi3Dhryf3F8vR370nN67pAERW+3a95vomb3MAREY= github.com/getkin/kin-openapi v0.127.0/go.mod h1:OZrfXzUfGrNbsKj+xmFBx6E5c6yH3At/tAKSc2UszXM= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/gzip v1.0.1 h1:HQ8ENHODeLY7a4g1Au/46Z92bdGFl74OhxcZble9WJE= github.com/gin-contrib/gzip v1.0.1/go.mod h1:njt428fdUNRvjuJf16tZMYZ2Yl+WQB53X5wmhDwXvC4= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= @@ -100,8 +101,12 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mark3labs/mcp-go v0.17.0 h1:5Ps6T7qXr7De/2QTqs9h6BKeZ/qdeUeGrgM5lPzi930= +github.com/mark3labs/mcp-go v0.17.0/go.mod h1:KmJndYv7GIgcPVwEKJjNcbhVQ+hJGJhrCCB/9xITzpE= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -148,6 +153,8 @@ github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65E github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/urfave/cli v1.22.16 h1:MH0k6uJxdwdeWQTwhSO42Pwr4YLrNLwBtg1MRgTqPdQ= github.com/urfave/cli v1.22.16/go.mod h1:EeJR6BKodywf4zciqrdw6hpCPk68JO9z5LazXZMn5Po= +github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4= +github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4= go.etcd.io/etcd/client/pkg/v3 v3.5.13 h1:RVZSAnWWWiI5IrYAXjQorajncORbS0zI48LQlE2kQWg= go.etcd.io/etcd/client/pkg/v3 v3.5.13/go.mod h1:XxHT4u1qU12E2+po+UVPrEeL94Um6zL58ppuJWXSAB8= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= diff --git a/mcp-server/openapi.go b/mcp-server/openapi.go new file mode 100644 index 00000000..b3a1c922 --- /dev/null +++ b/mcp-server/openapi.go @@ -0,0 +1,152 @@ +package mcp_server + +import ( + "encoding/json" + "net/http" + + "github.com/getkin/kin-openapi/openapi3" +) + +var ( + openapi3Loader = openapi3.NewLoader() +) + +type MCPInfo struct { + Name string + Description string + Apis []*API +} + +type API struct { + Path string `json:"path"` + Method string `json:"method"` + ContentType string `json:"content_type"` + Summary string `json:"summary"` + Description string `json:"description"` + Params []*openapi3.Parameter `json:"params"` + Body map[string]interface{} `json:"body"` +} + +func ConvertMCPFromOpenAPI3Data(data []byte) (*MCPInfo, error) { + spec, err := openapi3Loader.LoadFromData(data) + if err != nil { + return nil, err + } + return parseOpenAPI3(spec) +} + +func parseOpenAPI3(spec *openapi3.T) (*MCPInfo, error) { + items := spec.Paths.Map() + apis := make([]*API, 0, len(items)*4) + for path, item := range items { + pathApis, err := genAPIs(path, item) + if err != nil { + return nil, err + } + apis = append(apis, pathApis...) + } + + return &MCPInfo{ + Name: spec.Info.Title, + Description: spec.Info.Description, + Apis: apis, + }, nil +} + +var ( + validMethods = []string{ + http.MethodGet, + http.MethodPost, + http.MethodPut, + http.MethodPatch, + http.MethodDelete, + http.MethodHead, + http.MethodOptions, + } +) + +func genAPIs(path string, item *openapi3.PathItem) ([]*API, error) { + apis := make([]*API, 0, 8) + for _, method := range validMethods { + opt := item.GetOperation(method) + if opt == nil { + continue + } + api, err := genAPI(method, path, opt, item.Parameters) + if err != nil { + return nil, err + } + apis = append(apis, api) + } + return apis, nil +} + +func genAPI(method string, path string, opt *openapi3.Operation, params openapi3.Parameters) (*API, error) { + api := &API{ + Method: method, + Path: path, + Summary: opt.Summary, + Description: opt.Description, + Params: make([]*openapi3.Parameter, 0, len(params)+len(opt.Parameters)), + } + if api.Summary == "" { + api.Summary = opt.Description + } + parameters := make([]*openapi3.ParameterRef, 0, len(params)+len(opt.Parameters)) + parameters = append(parameters, opt.Parameters...) + parameters = append(parameters, params...) + for _, param := range parameters { + if param.Value != nil { + api.Params = append(api.Params, param.Value) + } + } + if opt.RequestBody != nil && opt.RequestBody.Value != nil && opt.RequestBody.Value.Content != nil { + for mediaType, media := range opt.RequestBody.Value.Content { + if media != nil && media.Schema != nil { + api.ContentType = mediaType + body, err := recurseSchemaRef(media.Schema) + if err != nil { + return nil, err + } + api.Body = body + } + } + } + + return api, nil +} + +func recurseSchemaRef(ref *openapi3.SchemaRef) (map[string]interface{}, error) { + if ref == nil || ref.Value == nil { + return nil, nil + } + data, err := json.Marshal(ref.Value) + if err != nil { + return nil, err + } + m := make(map[string]interface{}) + err = json.Unmarshal(data, &m) + if err != nil { + return nil, err + } + + if ref.Value.Properties != nil { + m["properties"] = make(map[string]interface{}) + for k, v := range ref.Value.Properties { + v, err := recurseSchemaRef(v) + if err != nil { + return nil, err + } + m["properties"].(map[string]interface{})[k] = v + } + } + if ref.Value.Items != nil { + v, err := recurseSchemaRef(ref.Value.Items) + if err != nil { + return nil, err + } + m["items"] = v + } + + return m, nil +} diff --git a/mcp-server/server.go b/mcp-server/server.go new file mode 100644 index 00000000..ed6cbd6f --- /dev/null +++ b/mcp-server/server.go @@ -0,0 +1,78 @@ +package mcp_server + +import ( + "fmt" + "net/http" + "strings" + + "github.com/mark3labs/mcp-go/server" + + "github.com/eolinker/eosc" +) + +var ( + mcpServer = NewServer() + ServiceBasePath = "mcp/service" + GlobalBasePath = "mcp/global" +) + +func NewServer() *Server { + return &Server{ + sseServers: eosc.BuildUntyped[string, *server.SSEServer](), + } +} + +type Server struct { + sseServers eosc.Untyped[string, *server.SSEServer] +} + +func (s *Server) Set(path string, sseServer *server.SSEServer) { + s.sseServers.Set(path, sseServer) +} + +func (s *Server) Del(path string) { + s.sseServers.Del(path) +} + +func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { + sseServer, has := s.sseServers.Get(trimPath(r.URL.Path)) + if has { + sseServer.ServeHTTP(w, r) + return + } + http.NotFound(w, r) + return +} + +func trimPath(path string) string { + path = strings.TrimSuffix(path, "/") + path = strings.TrimSuffix(path, "/message") + path = strings.TrimSuffix(path, "/sse") + return path +} + +func SetSSEServer(sid string, name string, version string, tools ...ITool) { + s := server.NewMCPServer(name, version) + for _, tool := range tools { + tool.RegisterMCP(s) + } + apiPath := fmt.Sprintf("/api/v1/%s/%s", ServiceBasePath, sid) + openAPIPath := fmt.Sprintf("/openapi/v1/%s/%s", ServiceBasePath, sid) + mcpServer.Set(apiPath, server.NewSSEServer(s, server.WithBasePath(apiPath))) + mcpServer.Set(openAPIPath, server.NewSSEServer(s, server.WithBasePath(openAPIPath))) +} + +func DelSSEServer(sid string) { + apiPath := fmt.Sprintf("/api/v1/%s/%s", ServiceBasePath, sid) + openAPIPath := fmt.Sprintf("/openapi/v1/%s/%s", ServiceBasePath, sid) + mcpServer.Del(apiPath) + mcpServer.Del(openAPIPath) +} + +func ServeHTTP(w http.ResponseWriter, r *http.Request) { + mcpServer.ServeHTTP(w, r) +} + +func DefaultMCPServer() *Server { + return mcpServer +} diff --git a/mcp-server/tool.go b/mcp-server/tool.go new file mode 100644 index 00000000..c91611c6 --- /dev/null +++ b/mcp-server/tool.go @@ -0,0 +1,130 @@ +package mcp_server + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "strings" + + "github.com/eolinker/go-common/utils" + + "github.com/mark3labs/mcp-go/mcp" + "github.com/mark3labs/mcp-go/server" +) + +type ITool interface { + RegisterMCP(s *server.MCPServer) +} + +const ( + MCPBody = "Body" + MCPHeader = "Header" + MCPQuery = "Query" + MCPPath = "Path" +) + +type Tool struct { + name string + url string + method string + contentType string + opts []mcp.ToolOption +} + +func NewTool(name string, uri string, method string, contentType string, opts ...mcp.ToolOption) ITool { + return &Tool{ + name: name, + url: uri, + method: method, + contentType: contentType, + opts: opts, + } +} + +func (t *Tool) RegisterMCP(s *server.MCPServer) { + s.AddTool(mcp.NewTool(t.name, t.opts...), func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + invokeAddress := utils.GatewayInvoke(ctx) + if invokeAddress == "" { + return nil, fmt.Errorf("invoke address is empty") + } + u, err := url.Parse(invokeAddress) + if err != nil { + return nil, fmt.Errorf("invalid invoke address %s", invokeAddress) + } + if u.Scheme == "" { + u.Scheme = "http" + } + + path := t.url + queries := url.Values{} + headers := make(map[string]string) + body := "" + for k, v := range request.Params.Arguments { + if k == "Body" { + tmp, _ := json.Marshal(v) + body = string(tmp) + continue + } + tmp, ok := v.(map[string]interface{}) + if !ok { + continue + } + switch k { + case MCPHeader: + for kk, vv := range tmp { + headers[kk] = fmt.Sprintf("%v", vv) + } + + case MCPQuery: + for kk, vv := range tmp { + queries.Set(kk, fmt.Sprintf("%v", vv)) + } + case MCPPath: + for kk, vv := range tmp { + p, ok := vv.(string) + if !ok { + return nil, fmt.Errorf("invalid path %s", v) + } + path = strings.Replace(path, fmt.Sprintf("{%s}", kk), p, -1) + } + } + } + u.Path = path + u.RawQuery = queries.Encode() + + req, err := http.NewRequest(t.method, u.String(), strings.NewReader(body)) + if err != nil { + return nil, err + } + for k, v := range headers { + req.Header.Set(k, v) + } + if t.contentType != "" { + req.Header.Set("Content-Type", t.contentType) + } + apikey := utils.Label(ctx, "apikey") + if apikey != "" { + req.Header.Set("Authorization", utils.Md5(apikey)) + } + + resp, err := client.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + d, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("status code %d, %s", resp.StatusCode, string(d)) + } + + return mcp.NewToolResultText(string(d)), nil + }) +} + +var client = http.Client{} diff --git a/module/ai-api/dto/input.go b/module/ai-api/dto/input.go index e559dd5f..4eeb624f 100644 --- a/module/ai-api/dto/input.go +++ b/module/ai-api/dto/input.go @@ -25,6 +25,7 @@ type AiPromptVariable struct { type AiModel struct { Id string `json:"id"` + Name string `json:"name"` Config string `json:"config"` Provider string `json:"provider"` Type string `json:"type"` diff --git a/module/ai-api/dto/output.go b/module/ai-api/dto/output.go index 521d8979..23e84e20 100644 --- a/module/ai-api/dto/output.go +++ b/module/ai-api/dto/output.go @@ -66,6 +66,7 @@ type APIItem struct { type ModelItem struct { Id string `json:"id"` + Name string `json:"name"` Logo string `json:"logo"` } diff --git a/module/ai-api/iml.go b/module/ai-api/iml.go index 58b9d884..25eda7f7 100644 --- a/module/ai-api/iml.go +++ b/module/ai-api/iml.go @@ -8,9 +8,12 @@ import ( "net/http" "strings" - "github.com/eolinker/eosc/log" + ai_provider_local "github.com/APIParkLab/APIPark/ai-provider/local" model_runtime "github.com/APIParkLab/APIPark/ai-provider/model-runtime" + ai_model "github.com/APIParkLab/APIPark/service/ai-model" + + "github.com/eolinker/eosc/log" ai_api_dto "github.com/APIParkLab/APIPark/module/ai-api/dto" ai_api "github.com/APIParkLab/APIPark/service/ai-api" @@ -32,11 +35,12 @@ var ( ) type imlAPIModule struct { - serviceService service.IServiceService `autowired:""` - apiDocService api_doc.IAPIDocService `autowired:""` - aiAPIService ai_api.IAPIService `autowired:""` - apiService api.IAPIService `autowired:""` - transaction store.ITransaction `autowired:""` + serviceService service.IServiceService `autowired:""` + apiDocService api_doc.IAPIDocService `autowired:""` + aiAPIService ai_api.IAPIService `autowired:""` + aiModelService ai_model.IProviderModelService `autowired:""` + apiService api.IAPIService `autowired:""` + transaction store.ITransaction `autowired:""` } func (i *imlAPIModule) getAPIDoc(ctx context.Context, serviceId string) (*openapi3.T, error) { @@ -237,26 +241,41 @@ func (i *imlAPIModule) List(ctx context.Context, keyword string, serviceId strin if err != nil { return item } - p, has := model_runtime.GetProvider(aiModel.Provider) - if has { - item.Provider = ai_api_dto.ProviderItem{ - Id: p.ID(), - Name: p.Name(), - Logo: p.Logo(), + item.ModelType = ai_api_dto.ModelType(aiModel.Type) + if item.ModelType == ai_api_dto.ModelTypeLocal { + item.Model = ai_api_dto.ModelItem{ + Id: aiModel.Id, + Name: aiModel.Id, } - m, has := p.GetModel(t.Model) - if has { - item.Model = ai_api_dto.ModelItem{ - Id: m.ID(), - Logo: m.Logo(), - } + item.Provider = ai_api_dto.ProviderItem{ + Id: ai_provider_local.ProviderLocal, + Name: ai_provider_local.ProviderLocal, + Logo: "", } } else { - - item.Model = ai_api_dto.ModelItem{ - Id: aiModel.Id, + p, has := model_runtime.GetProvider(aiModel.Provider) + if has { + item.Provider = ai_api_dto.ProviderItem{ + Id: p.ID(), + Name: p.Name(), + Logo: "", + } + m, has := p.GetModel(t.Model) + if has { + item.Model = ai_api_dto.ModelItem{ + Id: m.ID(), + Name: m.Name(), + Logo: "", + } + } + } else { + item.Model = ai_api_dto.ModelItem{ + Id: aiModel.Id, + Name: "unknown", + } } } + return item }), nil } @@ -281,6 +300,15 @@ func (i *imlAPIModule) Get(ctx context.Context, serviceId string, apiId string) if err != nil { return nil, err } + if aiModel.Name == "" { + // get provider model + modelInfo, _ := i.aiModelService.Get(ctx, aiModel.Id) + if modelInfo != nil { + aiModel.Name = modelInfo.Name + } else { + aiModel.Name = aiModel.Id + } + } return &ai_api_dto.API{ Id: apiInfo.ID, diff --git a/module/ai-api/schema.go b/module/ai-api/schema.go index 5c7c7c5c..083f2836 100644 --- a/module/ai-api/schema.go +++ b/module/ai-api/schema.go @@ -23,35 +23,18 @@ func genOperation(summary string, description string, variables []*ai_api_dto.Ai operation := openapi3.NewOperation() operation.Summary = summary operation.Description = description + operation.AddParameter(&openapi3.Parameter{ + Name: "Authorization", + In: "header", + Required: true, + Example: "{your_apipark_apikey}", + }) operation.RequestBody = genRequestBody(variables) operation.Responses = &openapi3.Responses{} operation.Responses.Set("200", genResponse()) return operation } -func genRequestParameters(variables []*ai_api_dto.AiPromptVariable) openapi3.Parameters { - return openapi3.Parameters{ - { - Value: &openapi3.Parameter{ - Name: "variables", - In: "body", - Description: "Replace the variable list of Prompt", // 替换Prompt的变量列表 - Schema: genVariableSchema(variables).NewRef(), - Required: true, - }, - }, - { - Value: &openapi3.Parameter{ - Name: "messages", - In: "body", - Description: "Chat Message", - Schema: messagesSchemaRef, - Required: true, - }, - }, - } -} - func genRequestBody(variables []*ai_api_dto.AiPromptVariable) *openapi3.RequestBodyRef { requestBody := openapi3.NewRequestBody() requestBody.Content = openapi3.NewContentWithSchema(genRequestBodySchema(variables), []string{"application/json"}) @@ -76,6 +59,10 @@ func genRequestBodySchema(variables []*ai_api_dto.AiPromptVariable) *openapi3.Sc result.WithProperty("variables", genVariableSchema(variables)) result.WithRequired([]string{"variables", "messages"}) } + streamSchema := openapi3.NewBoolSchema() + streamSchema.Title = "stream" + streamSchema.Description = "Whether to stream the response" + result.WithProperty("stream", streamSchema) result.WithPropertyRef("messages", messagesSchemaRef) @@ -156,17 +143,58 @@ func genMessagesSchema() *openapi3.Schema { func genResponseSchema() *openapi3.Schema { result := openapi3.NewObjectSchema() result.Description = "Response from the server" - result.WithPropertyRef("message", messageSchemaRef) - openapi3.NewIntegerSchema() - result.WithProperty("code", openapi3.NewIntegerSchema()) - result.WithProperty("error", openapi3.NewStringSchema()) - result.WithProperty("finish_reason", openapi3.NewStringSchema().WithEnum( + + // 创建 choices 数组 + choicesSchema := openapi3.NewArraySchema() + choiceItemSchema := openapi3.NewObjectSchema() + + // choice 中的 message 字段 + choiceItemSchema.WithPropertyRef("message", messageSchemaRef) + + // finish_reason 字段 + finishReasonSchema := openapi3.NewStringSchema().WithEnum( "stop", "length", "function_call", "content_filter", "null", - )) - + ) + choiceItemSchema.WithProperty("finish_reason", finishReasonSchema) + + // index 字段 + choiceItemSchema.WithProperty("index", openapi3.NewIntegerSchema()) + + // logprobs 字段,可以为 null + choiceItemSchema.WithProperty("logprobs", openapi3.NewSchema().WithNullable()) + + choicesSchema.Items = &openapi3.SchemaRef{Value: choiceItemSchema} + result.WithProperty("choices", choicesSchema) + + // object 字段 + result.WithProperty("object", openapi3.NewStringSchema().WithEnum("chat.completion")) + + // usage 字段 + usageSchema := openapi3.NewObjectSchema() + usageSchema.WithProperty("prompt_tokens", openapi3.NewIntegerSchema()) + usageSchema.WithProperty("completion_tokens", openapi3.NewIntegerSchema()) + usageSchema.WithProperty("total_tokens", openapi3.NewIntegerSchema()) + + // prompt_tokens_details 字段 + promptTokensDetailsSchema := openapi3.NewObjectSchema() + promptTokensDetailsSchema.WithProperty("cached_tokens", openapi3.NewIntegerSchema()) + usageSchema.WithProperty("prompt_tokens_details", promptTokensDetailsSchema) + + result.WithProperty("usage", usageSchema) + + // 其他字段 + result.WithProperty("created", openapi3.NewIntegerSchema()) + result.WithProperty("system_fingerprint", openapi3.NewStringSchema().WithNullable()) + result.WithProperty("model", openapi3.NewStringSchema()) + result.WithProperty("id", openapi3.NewStringSchema()) + + // 保留原有的错误字段 + result.WithProperty("code", openapi3.NewIntegerSchema()) + result.WithProperty("error", openapi3.NewStringSchema()) + return result } diff --git a/module/ai-balance/iml.go b/module/ai-balance/iml.go index 5ab66bc8..aab4c9a0 100644 --- a/module/ai-balance/iml.go +++ b/module/ai-balance/iml.go @@ -82,8 +82,8 @@ func (i *imlBalanceModule) Create(ctx context.Context, input *ai_balance_dto.Cre modelName = input.Model base = fmt.Sprintf("%s://%s", p.URI().Scheme(), p.URI().Host()) case ai_balance_dto.ModelTypeLocal: - input.Provider = "ollama" - providerName = "Ollama" + input.Provider = ai_provider_local.ProviderLocal + providerName = ai_provider_local.ProviderLocal modelName = input.Model v, has := i.settingService.Get(ctx, "system.ai_model.ollama_address") if !has { @@ -119,7 +119,7 @@ func newRelease(item *ai_balance.Balance, base string) *gateway.DynamicRelease { cfg := make(map[string]interface{}) cfg["provider"] = item.Provider cfg["model"] = item.Model - cfg["model_config"] = ai_provider_local.OllamaConfig + cfg["model_config"] = ai_provider_local.LocalConfig cfg["base"] = base cfg["priority"] = item.Priority return &gateway.DynamicRelease{ @@ -155,7 +155,7 @@ func (i *imlBalanceModule) Sort(ctx context.Context, input *ai_balance_dto.Sort) releases := make([]*gateway.DynamicRelease, 0, len(list)) for _, item := range list { base := v - if item.Provider != "ollama" { + if item.Provider != ai_provider_local.ProviderLocal { p, has := model_runtime.GetProvider(item.Provider) if !has { continue @@ -220,7 +220,8 @@ func (i *imlBalanceModule) Delete(ctx context.Context, id string) error { return i.syncGateway(ctx, cluster.DefaultClusterID, []*gateway.DynamicRelease{ { BasicItem: &gateway.BasicItem{ - ID: id, + ID: id, + Resource: "ai-provider", }, }, }, false) @@ -259,7 +260,7 @@ func (i *imlBalanceModule) syncGateway(ctx context.Context, clusterId string, re } func (i *imlBalanceModule) getLocalBalances(ctx context.Context, v string) ([]*gateway.DynamicRelease, error) { - balances, err := i.balanceService.Search(ctx, "", map[string]interface{}{"provider": "ollama"}, "priority asc") + balances, err := i.balanceService.Search(ctx, "", map[string]interface{}{"provider": ai_provider_local.ProviderLocal}, "priority asc") if err != nil { return nil, err } @@ -267,19 +268,20 @@ func (i *imlBalanceModule) getLocalBalances(ctx context.Context, v string) ([]*g var has bool v, has = i.settingService.Get(ctx, "system.ai_model.ollama_address") if !has { - return nil, fmt.Errorf("ollama address not found") + //return nil, fmt.Errorf("ollama address not found") + return nil, nil } } releases := make([]*gateway.DynamicRelease, 0, len(balances)) for _, item := range balances { base := v - if item.Provider != "ollama" { + if item.Provider != ai_provider_local.ProviderLocal { p, has := model_runtime.GetProvider(item.Provider) if !has { continue } - base = fmt.Sprintf("%s://%s", p.URI().Scheme(), p.URI().Host()) + base = fmt.Sprintf("%s://%s%s", p.URI().Scheme(), p.URI().Host(), p.URI().Path()) } releases = append(releases, newRelease(item, base)) } @@ -293,17 +295,22 @@ func (i *imlBalanceModule) getBalances(ctx context.Context) ([]*gateway.DynamicR } v, has := i.settingService.Get(ctx, "system.ai_model.ollama_address") if !has { - return nil, fmt.Errorf("ollama address not found") + //return nil, fmt.Errorf("ollama address not found") + return nil, nil } releases := make([]*gateway.DynamicRelease, 0, len(balances)) for _, item := range balances { base := v - if item.Provider != "ollama" { + if item.Provider != ai_provider_local.ProviderLocal { p, has := model_runtime.GetProvider(item.Provider) if !has { continue } - base = fmt.Sprintf("%s://%s", p.URI().Scheme(), p.URI().Host()) + base = fmt.Sprintf("%s://%s%s", p.URI().Scheme(), p.URI().Host(), p.URI().Path()) + } else { + if v == "" { + continue + } } releases = append(releases, newRelease(item, base)) } diff --git a/module/ai-key/iml.go b/module/ai-key/iml.go index 21cf216e..618dbb26 100644 --- a/module/ai-key/iml.go +++ b/module/ai-key/iml.go @@ -383,12 +383,12 @@ func (i *imlKeyModule) UpdateKeyStatus(ctx context.Context, providerId string, i } releases := []*gateway.DynamicRelease{{ BasicItem: &gateway.BasicItem{ - ID: id, + ID: fmt.Sprintf("%s-%s", providerId, id), Resource: "ai-key", }, Attr: nil, }} - return i.syncGateway(ctx, providerId, releases, false) + return i.syncGateway(ctx, cluster.DefaultClusterID, releases, false) } if info.Status == ai_key_dto.KeyDisable.Int() || info.Status == ai_key_dto.KeyExceed.Int() { // 超额 或 停用状态,可启用 @@ -411,7 +411,7 @@ func (i *imlKeyModule) UpdateKeyStatus(ctx context.Context, providerId string, i return err } releases := []*gateway.DynamicRelease{newKey(info)} - return i.syncGateway(ctx, providerId, releases, true) + return i.syncGateway(ctx, cluster.DefaultClusterID, releases, true) } return nil }) diff --git a/module/ai-local/iml.go b/module/ai-local/iml.go index 28577413..c61d3977 100644 --- a/module/ai-local/iml.go +++ b/module/ai-local/iml.go @@ -4,7 +4,7 @@ import ( "context" "errors" "fmt" - "strings" + "time" ai_balance "github.com/APIParkLab/APIPark/service/ai-balance" @@ -61,6 +61,7 @@ func (i *imlLocalModel) SyncLocalModels(ctx context.Context, address string) err if err != nil { return err } + return i.syncGateway(ctx, cluster.DefaultClusterID, releases, true) } @@ -73,8 +74,8 @@ func (i *imlLocalModel) SimpleList(ctx context.Context) ([]*ai_local_dto.SimpleI return &ai_local_dto.SimpleItem{ Id: s.Id, Name: s.Name, - DefaultConfig: ai_provider_local.OllamaConfig, - Logo: ai_provider_local.OllamaSvg, + DefaultConfig: ai_provider_local.LocalConfig, + Logo: ai_provider_local.LocalSvg, } }, func(l *ai_local.LocalModel) bool { if l.State != ai_local_dto.LocalModelStateNormal.Int() && l.State != ai_local_dto.LocalModelStateDisable.Int() { @@ -118,7 +119,7 @@ func (i *imlLocalModel) Search(ctx context.Context, keyword string) ([]*ai_local APICount: count, CanDelete: count < 1 && s.State != ai_local_dto.LocalModelStateDeploying.Int(), UpdateTime: auto.TimeLabel(s.UpdateAt), - Provider: "ollama", + Provider: ai_provider_local.ProviderLocal, } }), nil } @@ -247,9 +248,9 @@ func (i *imlLocalModel) pullHook(fn ...func() error) func(msg ai_provider_local. v, _ := i.settingService.Get(ctx, "system.ai_model.ollama_address") cfg := make(map[string]interface{}) - cfg["provider"] = "ollama" + cfg["provider"] = ai_provider_local.ProviderLocal cfg["model"] = msg.Model - cfg["model_config"] = ai_provider_local.OllamaConfig + cfg["model_config"] = ai_provider_local.LocalConfig cfg["priority"] = 0 cfg["base"] = v @@ -322,7 +323,7 @@ func (i *imlLocalModel) Deploy(ctx context.Context, model string, session string err = i.localModelService.Create(ctx, &ai_local.CreateLocalModel{ Id: model, Name: model, - Provider: "ollama", + Provider: ai_provider_local.ProviderLocal, State: ai_local_dto.LocalModelStateDeploying.Int(), }) @@ -449,9 +450,9 @@ func (i *imlLocalModel) Enable(ctx context.Context, model string) error { } v, _ := i.settingService.Get(ctx, "system.ai_model.ollama_address") cfg := make(map[string]interface{}) - cfg["provider"] = "ollama" + cfg["provider"] = ai_provider_local.ProviderLocal cfg["model"] = info.Id - cfg["model_config"] = ai_provider_local.OllamaConfig + cfg["model_config"] = ai_provider_local.LocalConfig cfg["priority"] = 0 cfg["base"] = v @@ -513,8 +514,8 @@ func (i *imlLocalModel) OnInit() { }) models, version := ai_provider_local.ModelsCanInstall() for _, model := range models { - delete(oldModels, model.Id) if v, ok := oldModels[model.Id]; ok { + delete(oldModels, model.Id) if v.Version == version { continue } @@ -542,6 +543,7 @@ func (i *imlLocalModel) OnInit() { return } } + } for id := range oldModels { err = i.localModelPackageService.Delete(ctx, id) @@ -549,29 +551,57 @@ func (i *imlLocalModel) OnInit() { return } } - installModels, err := ai_provider_local.ModelsInstalled() - if err != nil { - return - } - for _, model := range installModels { - - id := strings.TrimSuffix(model.Name, ":latest") - name := strings.TrimSuffix(model.Name, ":latest") - _, err = i.localModelService.Get(ctx, id) + //installModels, err := ai_provider_local.ModelsInstalled() + //if err != nil { + // return + //} + //for _, model := range installModels { + // + // id := strings.TrimSuffix(model.Name, ":latest") + // name := strings.TrimSuffix(model.Name, ":latest") + // _, err = i.localModelService.Get(ctx, id) + // if err != nil { + // if !errors.Is(err, gorm.ErrRecordNotFound) { + // return + // } + // err = i.localModelService.Create(ctx, &ai_local.CreateLocalModel{ + // Id: id, + // Name: name, + // State: 1, + // }) + // if err != nil { + // return + // } + // } + //} + i.transaction.Transaction(ctx, func(ctx context.Context) error { + localModels, err := i.localModelService.Search(ctx, "", map[string]interface{}{ + "provider": "ollama", + }) if err != nil { - if !errors.Is(err, gorm.ErrRecordNotFound) { - return - } - err = i.localModelService.Create(ctx, &ai_local.CreateLocalModel{ - Id: id, - Name: name, - State: 1, - }) - if err != nil { - return - } + return err } - } + if len(localModels) == 0 { + return nil + } + err = i.localModelService.UpdateProvider(ctx, ai_provider_local.ProviderLocal, utils.SliceToSlice(localModels, func(s *ai_local.LocalModel) string { + return s.Id + })...) + if err != nil { + return err + } + + apis, err := i.aiAPIService.Search(ctx, "", map[string]interface{}{ + "provider": "ollama", + }) + if err != nil { + return err + } + return i.aiAPIService.UpdateAIProvider(ctx, ai_provider_local.ProviderLocal, utils.SliceToSlice(apis, func(s *ai_api.API) string { + return s.ID + })...) + }) + }) } @@ -584,19 +614,20 @@ func (i *imlLocalModel) getLocalModels(ctx context.Context, v string) ([]*gatewa var has bool v, has = i.settingService.Get(ctx, "system.ai_model.ollama_address") if !has { - return nil, errors.New("ollama_address not set") + //return nil, errors.New("ollama_address not set") + return nil, nil } } - + provider := ai_provider_local.ProviderLocal releases := make([]*gateway.DynamicRelease, 0, len(list)) for _, l := range list { if l.State != ai_local_dto.LocalModelStateNormal.Int() { continue } cfg := make(map[string]interface{}) - cfg["provider"] = "ollama" + cfg["provider"] = provider cfg["model"] = l.Id - cfg["model_config"] = ai_provider_local.OllamaConfig + cfg["model_config"] = ai_provider_local.LocalConfig cfg["base"] = v releases = append(releases, &gateway.DynamicRelease{ BasicItem: &gateway.BasicItem{ @@ -611,6 +642,25 @@ func (i *imlLocalModel) getLocalModels(ctx context.Context, v string) ([]*gatewa Attr: cfg, }) } + releases = append(releases, &gateway.DynamicRelease{ + BasicItem: &gateway.BasicItem{ + ID: fmt.Sprintf("%s-key", ai_provider_local.ProviderLocal), + Description: "auto generate key", + Resource: "ai-key", + Version: time.Now().Format("20060102150405"), + MatchLabels: map[string]string{ + "module": "ai-key", + }, + }, + Attr: map[string]interface{}{ + "expired": 0, + "config": fmt.Sprintf("{\"base\":\"%s\"}", v), + "provider": provider, + "priority": 1, + "disabled": true, + }, + }) + return releases, nil } diff --git a/module/ai-model/iml.go b/module/ai-model/iml.go index 5b3564c1..26e1410e 100644 --- a/module/ai-model/iml.go +++ b/module/ai-model/iml.go @@ -1,15 +1,27 @@ package ai_model import ( + "context" + "errors" "fmt" + "slices" + "time" + + "gorm.io/gorm" + + "github.com/APIParkLab/APIPark/service/cluster" + + "github.com/APIParkLab/APIPark/gateway" + model_runtime "github.com/APIParkLab/APIPark/ai-provider/model-runtime" model_dto "github.com/APIParkLab/APIPark/module/ai-model/dto" "github.com/APIParkLab/APIPark/service/ai" + ai_api "github.com/APIParkLab/APIPark/service/ai-api" ai_model "github.com/APIParkLab/APIPark/service/ai-model" "github.com/gin-gonic/gin" "github.com/google/uuid" - "slices" + "github.com/eolinker/eosc/log" "github.com/eolinker/go-common/store" ) @@ -19,7 +31,9 @@ var ( type imlProviderModelModule struct { providerService ai.IProviderService `autowired:""` + aiApiService ai_api.IAPIService `autowired:""` providerModelService ai_model.IProviderModelService `autowired:""` + clusterService cluster.IClusterService `autowired:""` transaction store.ITransaction `autowired:""` } @@ -48,55 +62,89 @@ func (i *imlProviderModelModule) UpdateProviderModel(ctx *gin.Context, provider return fmt.Errorf("ai provider not found") } // check provider exist - providerInfo, err := i.providerService.Get(ctx, provider) + _, err := i.providerService.Get(ctx, provider) if err != nil { return err } - if providerInfo == nil { - return fmt.Errorf("provider not found") - } modelInfo, _ := i.providerModelService.Get(ctx, input.Id) if modelInfo == nil || modelInfo.Provider != provider { return fmt.Errorf("model not found") } - // check model name duplicate - if has := i.providerModelService.CheckNameDuplicate(ctx, provider, input.Name, input.Id); has { - return fmt.Errorf("model name: `%s` duplicate", input.Name) - } - if err := i.providerModelService.Save(ctx, input.Id, &ai_model.Model{ - Name: &input.Name, - AccessConfiguration: &input.AccessConfiguration, - ModelParameters: &input.ModelParameters, - }); err != nil { - return err - } - // update provider model - iModel, _ := model_runtime.NewCustomizeModel(input.Id, input.Name, p.Logo(), input.AccessConfiguration, input.ModelParameters) - p.SetModel(input.Id, iModel) + return i.transaction.Transaction(ctx, func(ctx context.Context) error { + if err = i.providerModelService.Save(ctx, input.Id, &ai_model.Model{ + AccessConfiguration: &input.AccessConfiguration, + ModelParameters: &input.ModelParameters, + }); err != nil { + return err + } + + // update provider model + iModel, err := model_runtime.NewCustomizeModel(input.Id, input.Name, p.Logo(), input.AccessConfiguration, input.ModelParameters) + if err != nil { + return err + } + // 判断是否需要发布model + if p.GetModelConfig().AccessConfigurationStatus { + if err := i.syncGateway(ctx, cluster.DefaultClusterID, []*gateway.DynamicRelease{ + newModel(provider, input.Name, input.AccessConfiguration), + }, true); err != nil { + return err + } + } + + p.SetModel(input.Id, iModel) + return nil + }) return nil } func (i *imlProviderModelModule) DeleteProviderModel(ctx *gin.Context, provider string, id string) error { p, has := model_runtime.GetProvider(provider) - // check provider exist - providerInfo, err := i.providerService.Get(ctx, provider) - if err != nil { - return err + if !has { + return fmt.Errorf("ai provider not found") } - if providerInfo == nil || !has { - return fmt.Errorf("provider not found") + // check provider exist + _, err := i.providerService.Get(ctx, provider) + if err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + return fmt.Errorf("provider not found") + } + return err } modelInfo, _ := i.providerModelService.Get(ctx, id) if modelInfo == nil || modelInfo.Provider != provider { return fmt.Errorf("model not found") } - if err := i.providerModelService.Delete(ctx, id); err != nil { - return err - } - p.RemoveModel(id) + return i.transaction.Transaction(ctx, func(ctx context.Context) error { + // check model in use + count, err := i.aiApiService.CountByModel(ctx, id) + if err != nil { + return err + } + if count > 0 { + return fmt.Errorf("model in use") + } + if err := i.providerModelService.Delete(ctx, id); err != nil { + return err + } + err = i.syncGateway(ctx, cluster.DefaultClusterID, []*gateway.DynamicRelease{ + { + BasicItem: &gateway.BasicItem{ + ID: fmt.Sprintf("%s#%s", provider, modelInfo.Name), + Resource: "ai-model", + }, + Attr: nil, + }, + }, false) + if err != nil { + return err + } + + p.RemoveModel(id) + return nil + }) - return nil } func (i *imlProviderModelModule) AddProviderModel(ctx *gin.Context, provider string, input *model_dto.Model) (*model_dto.SimpleModel, error) { @@ -104,34 +152,99 @@ func (i *imlProviderModelModule) AddProviderModel(ctx *gin.Context, provider str if !has { return nil, fmt.Errorf("ai provider not found") } - // check provider exist - providerInfo, err := i.providerService.Get(ctx, provider) - if err != nil { - return nil, err - } - if providerInfo == nil { - return nil, fmt.Errorf("provider not found") - } // check model name duplicate if has := i.providerModelService.CheckNameDuplicate(ctx, provider, input.Name, ""); has { return nil, fmt.Errorf("model name: `%s` duplicate", input.Name) } + // check provider model exist + if _, has := p.GetModel(input.Name); has { + return nil, fmt.Errorf("provider model already exist") + } id := uuid.New().String() - typeValue := "chat" - if err := i.providerModelService.Save(ctx, id, &ai_model.Model{ - Name: &input.Name, - Type: &typeValue, - Provider: &provider, - AccessConfiguration: &input.AccessConfiguration, - ModelParameters: &input.ModelParameters, - }); err != nil { + err := i.transaction.Transaction(ctx, func(ctx context.Context) error { + typeValue := "chat" + err := i.providerModelService.Save(ctx, id, &ai_model.Model{ + Name: &input.Name, + Type: &typeValue, + Provider: &provider, + AccessConfiguration: &input.AccessConfiguration, + ModelParameters: &input.ModelParameters, + }) + if err != nil { + return err + } + // update provider model + iModel, err := model_runtime.NewCustomizeModel(id, input.Name, p.Logo(), input.AccessConfiguration, input.ModelParameters) + if err != nil { + return err + } + // 判断是否需要发布model + if p.GetModelConfig().AccessConfigurationStatus { + if err := i.syncGateway(ctx, cluster.DefaultClusterID, []*gateway.DynamicRelease{ + newModel(provider, input.Name, input.AccessConfiguration), + }, true); err != nil { + return err + } + } + + p.SetModel(id, iModel) + return nil + }) + if err != nil { return nil, err } - // update provider model - iModel, _ := model_runtime.NewCustomizeModel(id, input.Name, p.Logo(), input.AccessConfiguration, input.ModelParameters) - p.SetModel(id, iModel) return &model_dto.SimpleModel{ Id: id, Name: input.Name, }, nil } + +func newModel(provider string, model string, config string) *gateway.DynamicRelease { + + return &gateway.DynamicRelease{ + BasicItem: &gateway.BasicItem{ + ID: fmt.Sprintf("%s$%s", provider, model), + Description: fmt.Sprintf("auto generated model: %s, provider: %s", model, provider), + Resource: "ai-model", + Version: time.Now().Format("20060102150405"), + MatchLabels: map[string]string{ + "module": "ai-model", + }, + }, + Attr: map[string]interface{}{ + "provider": provider, + "model": model, + "access_config": config, + }, + } +} + +func (i *imlProviderModelModule) syncGateway(ctx context.Context, clusterId string, releases []*gateway.DynamicRelease, online bool) error { + client, err := i.clusterService.GatewayClient(ctx, clusterId) + if err != nil { + log.Errorf("get apinto client error: %v", err) + return nil + } + defer func() { + err := client.Close(ctx) + if err != nil { + log.Warn("close apinto client:", err) + } + }() + for _, releaseInfo := range releases { + dynamicClient, err := client.Dynamic(releaseInfo.Resource) + if err != nil { + return err + } + if online { + err = dynamicClient.Online(ctx, releaseInfo) + } else { + err = dynamicClient.Offline(ctx, releaseInfo) + } + if err != nil { + return err + } + } + + return nil +} diff --git a/module/ai/dto/output.go b/module/ai/dto/output.go index d768f5bd..d87aca1c 100644 --- a/module/ai/dto/output.go +++ b/module/ai/dto/output.go @@ -37,15 +37,16 @@ type ModelConfig struct { } type ConfiguredProviderItem struct { - Id string `json:"id"` - Name string `json:"name"` - Logo string `json:"logo"` - DefaultLLM string `json:"default_llm"` - Status ProviderStatus `json:"status"` - APICount int64 `json:"api_count"` - KeyCount int64 `json:"key_count"` - ModelCount int64 `json:"model_count"` - CanDelete bool `json:"can_delete"` + Id string `json:"id"` + Name string `json:"name"` + Logo string `json:"logo"` + DefaultLLM string `json:"default_llm"` + DefaultLLMName string `json:"default_llm_name"` + Status ProviderStatus `json:"status"` + APICount int64 `json:"api_count"` + KeyCount int64 `json:"key_count"` + ModelCount int64 `json:"model_count"` + CanDelete bool `json:"can_delete"` } type KeyStatus struct { diff --git a/module/ai/iml.go b/module/ai/iml.go index 665051ec..d3d9b535 100644 --- a/module/ai/iml.go +++ b/module/ai/iml.go @@ -4,12 +4,14 @@ import ( "context" "errors" "fmt" - ai_model "github.com/APIParkLab/APIPark/service/ai-model" - "github.com/google/uuid" "net/http" "sort" + "strings" "time" + ai_model "github.com/APIParkLab/APIPark/service/ai-model" + "github.com/google/uuid" + ai_provider_local "github.com/APIParkLab/APIPark/ai-provider/local" "github.com/eolinker/go-common/register" @@ -104,7 +106,9 @@ func (i *imlProviderModule) OnInit() { } } else { provider, _ := model_runtime.NewCustomizeProvider(p.Id, p.Name, iModels, p.DefaultLLM, p.Config) - model_runtime.Register(p.Id, provider) + if provider != nil { + model_runtime.Register(p.Id, provider) + } } } i.transaction.Transaction(ctx, func(ctx context.Context) error { @@ -169,12 +173,14 @@ func (i *imlProviderModule) Delete(ctx context.Context, id string) error { return err } + // delete register customize provider + if p, _ := i.providerService.Get(ctx, id); p != nil && p.Type != 0 { + model_runtime.Remove(id) + } err = i.providerService.Delete(ctx, id) if err != nil { return err } - // delete register provider - model_runtime.Remove(id) releases := make([]*gateway.DynamicRelease, 0, len(keys)) for _, key := range keys { releases = append(releases, newKey(key)) @@ -195,13 +201,17 @@ func (i *imlProviderModule) Delete(ctx context.Context, id string) error { } func (i *imlProviderModule) AddProvider(ctx context.Context, input *ai_dto.NewProvider) (*ai_dto.SimpleProvider, error) { - if has := i.providerService.CheckNameDuplicate(ctx, input.Name); has { + _, has := model_runtime.GetProvider(strings.ToLower(input.Name)) + if has { + return nil, fmt.Errorf("provider `%s` duplicate", input.Name) + } + // uuid = name + if has := i.providerService.CheckUuidDuplicate(ctx, input.Name); has { return nil, fmt.Errorf("provider `%s` duplicate", input.Name) } - id := uuid.New().String() config, defaultLLM := "{\"base_url\": \"\", \"api_key\": \"\"}", "" if err := i.providerService.Create(ctx, &ai.CreateProvider{ - Id: id, + Id: input.Name, Name: input.Name, DefaultLLM: defaultLLM, Config: config, @@ -210,13 +220,13 @@ func (i *imlProviderModule) AddProvider(ctx context.Context, input *ai_dto.NewPr return nil, err } // register provider - iProvider, _ := model_runtime.NewCustomizeProvider(id, input.Name, []model_runtime.IModel{}, "", "") - model_runtime.Register(id, iProvider) + iProvider, _ := model_runtime.NewCustomizeProvider(input.Name, input.Name, []model_runtime.IModel{}, "", "") + model_runtime.Register(input.Name, iProvider) return &ai_dto.SimpleProvider{ - Id: id, + Id: input.Name, Name: input.Name, DefaultConfig: config, - Logo: model_runtime.GetCustomizeLogo(), + Logo: iProvider.Logo(), }, nil } @@ -236,7 +246,7 @@ func (i *imlProviderModule) SimpleProvider(ctx context.Context, id string) (*ai_ func (i *imlProviderModule) ConfiguredProviders(ctx context.Context, keyword string) ([]*ai_dto.ConfiguredProviderItem, error) { // 获取已配置的AI服务商 - list, err := i.providerService.Search(ctx, keyword, nil, "update_at") + list, err := i.providerService.Search(ctx, keyword, nil, "update_at desc") if err != nil { return nil, fmt.Errorf("get provider list error:%v", err) } @@ -277,17 +287,21 @@ func (i *imlProviderModule) ConfiguredProviders(ctx context.Context, keyword str continue } apiCount := aiAPIMap[l.Id] - + defaultLLMName := "" + if defaultModel, has := p.GetModel(l.DefaultLLM); has { + defaultLLMName = defaultModel.Name() + } providers = append(providers, &ai_dto.ConfiguredProviderItem{ - Id: l.Id, - Name: l.Name, - Logo: p.Logo(), - DefaultLLM: l.DefaultLLM, - Status: ai_dto.ToProviderStatus(l.Status), - APICount: apiCount, - KeyCount: keyMap[l.Id], - CanDelete: apiCount < 1, - ModelCount: int64(len(p.Models())), + Id: l.Id, + Name: l.Name, + Logo: p.Logo(), + DefaultLLM: l.DefaultLLM, + DefaultLLMName: defaultLLMName, + Status: ai_dto.ToProviderStatus(l.Status), + APICount: apiCount, + KeyCount: keyMap[l.Id], + CanDelete: apiCount < 1, + ModelCount: int64(len(p.Models())), }) } @@ -334,11 +348,11 @@ func (i *imlProviderModule) SimpleConfiguredProviders(ctx context.Context, all b healthProvider := make(map[string]struct{}) if all { - healthProvider["ollama"] = struct{}{} + healthProvider[ai_provider_local.ProviderLocal] = struct{}{} items = append(items, &ai_dto.SimpleProviderItem{ - Id: "ollama", - Name: "Ollama", - Logo: ai_provider_local.OllamaSvg, + Id: ai_provider_local.ProviderLocal, + Name: ai_provider_local.ProviderLocal, + Logo: ai_provider_local.LocalSvg, Configured: true, DefaultConfig: "", Status: ai_dto.ProviderEnabled, @@ -447,7 +461,7 @@ func (i *imlProviderModule) Provider(ctx context.Context, id string) (*ai_dto.Pr if !has { return nil, fmt.Errorf("ai provider not found") } - + providerModelConfig := p.GetModelConfig() info, err := i.providerService.Get(ctx, id) if err != nil { if !errors.Is(err, gorm.ErrRecordNotFound) { @@ -457,7 +471,6 @@ func (i *imlProviderModule) Provider(ctx context.Context, id string) (*ai_dto.Pr if !has { defaultLLM, _ = model_runtime.NewCustomizeModel("", "", "", "", "") } - providerModelConfig := p.GetModelConfig() return &ai_dto.Provider{ Id: p.ID(), Name: p.Name(), @@ -479,6 +492,8 @@ func (i *imlProviderModule) Provider(ctx context.Context, id string) (*ai_dto.Pr model, has := p.DefaultModel(model_runtime.ModelTypeLLM) if !has || model == nil { defaultLLM, _ = model_runtime.NewCustomizeModel("", "", "", "", "") + } else { + defaultLLM = model } } @@ -494,8 +509,8 @@ func (i *imlProviderModule) Provider(ctx context.Context, id string) (*ai_dto.Pr Configured: true, Type: info.Type, ModelConfig: ai_dto.ModelConfig{ - AccessConfigurationStatus: false, - AccessConfigurationDemo: "", + AccessConfigurationStatus: providerModelConfig.AccessConfigurationStatus, + AccessConfigurationDemo: providerModelConfig.AccessConfigurationDemo, }, }, nil } @@ -586,7 +601,7 @@ func (i *imlProviderModule) UpdateProviderConfig(ctx context.Context, id string, return err } } - _, has := p.GetModel(input.DefaultLLM) + model, has := p.GetModel(input.DefaultLLM) if !has { return fmt.Errorf("ai provider model not found") } @@ -643,11 +658,12 @@ func (i *imlProviderModule) UpdateProviderConfig(ctx context.Context, id string, } // customize provider if info.Type == 1 { - if uri, uriErr := model_runtime.GetCustomizeProviderURI(input.Config, false); uriErr != nil { - p.SetURI(uri) + uri, uriErr := model_runtime.GetCustomizeProviderURI(input.Config, false) + if uriErr != nil { + return uriErr } + p.SetURI(uri) } - /** if *pInfo.Status == 0 { return i.syncGateway(ctx, cluster.DefaultClusterID, []*gateway.DynamicRelease{ { @@ -665,9 +681,9 @@ func (i *imlProviderModule) UpdateProviderConfig(ctx context.Context, id string, } cfg := make(map[string]interface{}) cfg["provider"] = info.Id - cfg["model"] = info.DefaultLLM + cfg["model"] = model.Name() cfg["model_config"] = model.DefaultConfig() - cfg["base"] = fmt.Sprintf("%s://%s", p.URI().Scheme(), p.URI().Host()) + cfg["base"] = fmt.Sprintf("%s://%s%s", p.URI().Scheme(), p.URI().Host(), p.URI().Path()) return i.syncGateway(ctx, cluster.DefaultClusterID, []*gateway.DynamicRelease{ { BasicItem: &gateway.BasicItem{ @@ -682,9 +698,7 @@ func (i *imlProviderModule) UpdateProviderConfig(ctx context.Context, id string, Attr: cfg, }, newKey(defaultKey), }, true) - */ - return nil }) } @@ -787,7 +801,7 @@ type imlAIApiModule struct { func (i *imlAIApiModule) APIs(ctx context.Context, keyword string, providerId string, start int64, end int64, page int, pageSize int, sortCondition string, asc bool, models []string, serviceIds []string) ([]*ai_dto.APIItem, *ai_dto.Condition, int64, error) { modelItems := make([]*ai_dto.BasicInfo, 0) - if providerId == "ollama" { + if providerId == ai_provider_local.ProviderLocal { items, err := i.aiLocalModelService.Search(ctx, "", nil, "update_at desc") if err != nil { return nil, nil, 0, err diff --git a/module/application-authorization/iml.go b/module/application-authorization/iml.go index 2dae5943..391eb16d 100644 --- a/module/application-authorization/iml.go +++ b/module/application-authorization/iml.go @@ -110,7 +110,7 @@ func (i *imlAuthorizationModule) getApplications(ctx context.Context, appIds []s } func (i *imlAuthorizationModule) initGateway(ctx context.Context, partitionId string, clientDriver gateway.IClientDriver) error { - services, err := i.serviceService.List(ctx) + services, err := i.serviceService.AppList(ctx) if err != nil { return err } diff --git a/module/catalogue/catalogue.go b/module/catalogue/catalogue.go index c665d681..ad41e5e8 100644 --- a/module/catalogue/catalogue.go +++ b/module/catalogue/catalogue.go @@ -3,6 +3,7 @@ package catalogue import ( "context" "reflect" + "time" "github.com/APIParkLab/APIPark/module/system" @@ -47,3 +48,9 @@ func init() { return reflect.ValueOf(catalogueModule) }) } + +func formatTimeByMinute(org int64) time.Time { + t := time.Unix(org, 0) + location, _ := time.LoadLocation("Asia/Shanghai") + return time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), 0, 0, location) +} diff --git a/module/catalogue/dto/output.go b/module/catalogue/dto/output.go index ef84220b..615504d1 100644 --- a/module/catalogue/dto/output.go +++ b/module/catalogue/dto/output.go @@ -18,15 +18,19 @@ type ServiceItem struct { Logo string `json:"logo"` ApiNum int64 `json:"api_num"` SubscriberNum int64 `json:"subscriber_num"` + InvokeCount int64 `json:"invoke_count"` + EnableMCP bool `json:"enable_mcp"` + ServiceKind string `json:"service_kind"` } type ServiceDetail struct { - Name string `json:"name"` - Description string `json:"description"` - Document string `json:"document"` - Basic *ServiceBasic `json:"basic"` - //Apis []*ServiceApi `json:"apis"` - APIDoc string `json:"api_doc"` + Name string `json:"name"` + Description string `json:"description"` + Document string `json:"document"` + Basic *ServiceBasic `json:"basic"` + APIDoc string `json:"api_doc"` + MCPServerAddress string `json:"mcp_server_address"` + MCPAccessConfig string `json:"mcp_access_config"` } type ServiceBasic struct { @@ -42,6 +46,7 @@ type ServiceBasic struct { ServiceKind string `json:"service_kind"` InvokeAddress string `json:"invoke_address"` SitePrefix string `json:"site_prefix"` + EnableMCP bool `json:"enable_mcp"` } type ServiceApiBasic struct { diff --git a/module/catalogue/iml.go b/module/catalogue/iml.go index f09cf820..c4af0785 100644 --- a/module/catalogue/iml.go +++ b/module/catalogue/iml.go @@ -6,6 +6,14 @@ import ( "fmt" "math" "sort" + "strings" + "time" + + mcp_server "github.com/APIParkLab/APIPark/mcp-server" + + "github.com/APIParkLab/APIPark/module/monitor/driver" + + "github.com/APIParkLab/APIPark/service/monitor" "github.com/APIParkLab/APIPark/service/setting" @@ -63,6 +71,7 @@ type imlCatalogueModule struct { transaction store.ITransaction `autowired:""` clusterService cluster.IClusterService `autowired:""` settingService setting.ISettingService `autowired:""` + monitorService monitor.IMonitorService `autowired:""` root *Root } @@ -124,6 +133,84 @@ func (i *imlCatalogueModule) ExportAll(ctx context.Context) ([]*catalogue_dto.Ex }), nil } +func (i *imlCatalogueModule) getExecutor(ctx context.Context, clusterId string) (driver.IExecutor, error) { + info, err := i.monitorService.GetByCluster(ctx, clusterId) + if err != nil { + return nil, err + } + return driver.CreateExecutor(info.Driver, info.Config) +} + +func (i *imlCatalogueModule) statistics(ctx context.Context, clusterId string, groupBy string, start, end time.Time, wheres []monitor.MonWhereItem, limit int) (map[string]monitor.MonCommonData, error) { + executor, err := i.getExecutor(ctx, clusterId) + if err != nil { + return nil, err + } + result, err := executor.CommonStatistics(ctx, start, end, groupBy, limit, wheres) + if err != nil { + return nil, err + } + return result, nil +} + +func (i *imlCatalogueModule) genCommonWheres(ctx context.Context, clusterIds ...string) ([]monitor.MonWhereItem, error) { + + clusters, err := i.clusterService.List(ctx, clusterIds...) + if err != nil { + return nil, err + } + clusterIds = utils.SliceToSlice(clusters, func(item *cluster.Cluster) string { + return item.Uuid + }) + + wheres := make([]monitor.MonWhereItem, 0, 1) + nodes, err := i.clusterService.Nodes(ctx, clusterIds...) + if err != nil { + return nil, err + } + nodeIds := utils.SliceToSlice(nodes, func(s *cluster.Node) string { + return s.Name + }) + wheres = append(wheres, monitor.MonWhereItem{ + Key: "node", + Operation: "in", + Values: nodeIds, + }) + + return wheres, nil +} + +func (i *imlCatalogueModule) ProviderStatistics(ctx context.Context, start, end time.Time, serviceIds ...string) (map[string]int64, error) { + clusterId := cluster.DefaultClusterID + _, err := i.clusterService.Get(ctx, clusterId) + if err != nil { + return nil, err + } + + wheres, err := i.genCommonWheres(ctx, clusterId) + if err != nil { + return nil, err + } + + if len(serviceIds) > 0 { + wheres = append(wheres, monitor.MonWhereItem{ + Key: "provider", + Operation: "in", + Values: serviceIds, + }) + } + statisticMap, err := i.statistics(ctx, clusterId, "provider", start, end, wheres, 0) + if err != nil { + return nil, err + } + resultMap := make(map[string]int64) + for key, item := range statisticMap { + resultMap[key] = item.RequestTotal + } + + return resultMap, nil +} + func (i *imlCatalogueModule) Subscribe(ctx context.Context, subscribeInfo *catalogue_dto.SubscribeService) error { if len(subscribeInfo.Applications) == 0 { return fmt.Errorf("applications is empty") @@ -226,9 +313,17 @@ func (i *imlCatalogueModule) Subscribe(ctx context.Context, subscribeInfo *catal } return nil }) - } +var mcpDefaultConfig = `{ + "mcpServers": { + "%s": { + "url": "%s" + } + } +} +` + func (i *imlCatalogueModule) ServiceDetail(ctx context.Context, sid string) (*catalogue_dto.ServiceDetail, error) { // 获取服务的基本信息 s, err := i.serviceService.Get(ctx, sid) @@ -290,7 +385,15 @@ func (i *imlCatalogueModule) ServiceDetail(ctx context.Context, sid string) (*ca } invokeAddress, _ := i.settingService.Get(ctx, setting.KeyInvokeAddress) sitePrefix, _ := i.settingService.Get(ctx, setting.KeySitePrefix) + mcpAccessAddress := "" + mcpAccessConfig := "" + if s.EnableMCP { + if sitePrefix != "" { + mcpAccessAddress = fmt.Sprintf("%s/openapi/v1/%s/%s/sse?apikey={your_api_key}", strings.TrimSuffix(sitePrefix, "/"), mcp_server.ServiceBasePath, s.Id) + mcpAccessConfig = fmt.Sprintf(mcpDefaultConfig, fmt.Sprintf("APIPark/%s", s.Name), mcpAccessAddress) + } + } return &catalogue_dto.ServiceDetail{ Name: s.Name, Description: s.Description, @@ -308,8 +411,11 @@ func (i *imlCatalogueModule) ServiceDetail(ctx context.Context, sid string) (*ca ServiceKind: s.Kind.String(), InvokeAddress: invokeAddress, SitePrefix: sitePrefix, + EnableMCP: s.EnableMCP, }, - APIDoc: apiDoc, + APIDoc: apiDoc, + MCPServerAddress: mcpAccessAddress, + MCPAccessConfig: mcpAccessConfig, }, nil } @@ -348,16 +454,14 @@ func (i *imlCatalogueModule) Services(ctx context.Context, keyword string) ([]*c return nil, err } - //// 获取服务API数量 - //apiCountMap, err := i.apiDocService.LatestAPICountByServices(ctx, serviceIds...) - //if err != nil { - // return nil, err - //} - subscriberCountMap, err := i.subscribeService.CountMapByService(ctx, subscribe.ApplyStatusSubscribe, serviceIds...) if err != nil { return nil, err } + invokeStatisticMap, err := i.ProviderStatistics(ctx, time.Now().Add(-24*30*time.Hour), time.Now(), serviceIds...) + if err != nil { + return nil, err + } result := make([]*catalogue_dto.ServiceItem, 0, len(items)) for _, v := range items { @@ -375,6 +479,9 @@ func (i *imlCatalogueModule) Services(ctx context.Context, keyword string) ([]*c SubscriberNum: subscriberCountMap[v.Id], Description: v.Description, Logo: v.Logo, + EnableMCP: v.EnableMCP, + ServiceKind: v.Kind.String(), + InvokeCount: invokeStatisticMap[v.Id], }) } sort.Slice(result, func(i, j int) bool { diff --git a/module/mcp/dto/output.go b/module/mcp/dto/output.go new file mode 100644 index 00000000..78f7f13b --- /dev/null +++ b/module/mcp/dto/output.go @@ -0,0 +1,44 @@ +package mcp_dto + +import ( + "time" +) + +type Service struct { + Id string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + ServiceKind string `json:"service_kind"` + Apis []*API `json:"apis"` + CreateTime time.Time `json:"create_time"` + UpdateTime time.Time `json:"update_time"` +} + +type App struct { + Id string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + CreateTime time.Time `json:"create_time"` + UpdateTime time.Time `json:"update_time"` +} + +type API struct { + Name string `json:"name"` + Method string `json:"method"` + Path string `json:"path"` + Description string `json:"description"` +} + +type ServiceAPI struct { + ServiceID string `json:"service_id"` + ServiceName string `json:"service_name"` + APIDoc interface{} `json:"api_doc"` +} + +type AppAuthorization struct { + Id string `json:"id"` + Name string `json:"name"` + Position string `json:"position"` + TokenName string `json:"token_name"` + Config string `json:"config"` +} diff --git a/module/mcp/iml.go b/module/mcp/iml.go new file mode 100644 index 00000000..8ae2ac23 --- /dev/null +++ b/module/mcp/iml.go @@ -0,0 +1,328 @@ +package mcp + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "strings" + "time" + + "github.com/APIParkLab/APIPark/service/subscribe" + + "github.com/getkin/kin-openapi/openapi3" + + "gorm.io/gorm" + + "github.com/APIParkLab/APIPark/service/release" + + mcp_dto "github.com/APIParkLab/APIPark/module/mcp/dto" + "github.com/eolinker/go-common/utils" + + api_doc "github.com/APIParkLab/APIPark/service/api-doc" + + application_authorization "github.com/APIParkLab/APIPark/service/application-authorization" + + "github.com/APIParkLab/APIPark/service/service" + + "github.com/mark3labs/mcp-go/mcp" +) + +var _ IMcpModule = (*imlMcpModule)(nil) + +var ( + openapi3Loader = openapi3.NewLoader() +) + +type imlMcpModule struct { + serviceService service.IServiceService `autowired:""` + appService service.IServiceService `autowired:""` + appAuthorizationService application_authorization.IAuthorizationService `autowired:""` + apiDocService api_doc.IAPIDocService `autowired:""` + subscriberService subscribe.ISubscribeService `autowired:""` + releaseService release.IReleaseService `autowired:""` +} + +func (i *imlMcpModule) Services(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { + keyword, _ := req.Params.Arguments["keyword"].(string) + list, err := i.serviceService.Search(ctx, keyword, map[string]interface{}{ + "as_server": true, + }, "update_at desc") + if err != nil { + return nil, fmt.Errorf("search service error: %w", err) + } + if len(list) == 0 { + list, err = i.serviceService.Search(ctx, "", map[string]interface{}{ + "as_server": true, + }, "update_at desc") + if err != nil { + return nil, fmt.Errorf("search service error: %w", err) + } + } + result := make([]*mcp_dto.Service, 0, len(list)) + for _, s := range list { + serviceRelease, err := i.releaseService.GetRunning(ctx, s.Id) + if err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + return nil, fmt.Errorf("get service release error: %w,service id is %s", err, s.Id) + } + continue + } + _, _, apiDocRelease, _, _, err := i.releaseService.GetReleaseInfos(ctx, serviceRelease.UUID) + if err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + return nil, fmt.Errorf("get service release info error: %w,service id is %s", err, s.Id) + } + continue + } + commit, err := i.apiDocService.GetDocCommit(ctx, apiDocRelease.Commit) + if err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + return nil, fmt.Errorf("get api doc release error: %w,service id is %s", err, s.Id) + } + continue + } + T, err := openapi3Loader.LoadFromData([]byte(commit.Data.Content)) + if err != nil { + return nil, fmt.Errorf("load openapi3 error: %w,service id is %s", err, s.Id) + } + apis := make([]*mcp_dto.API, 0, len(T.Paths.Map())) + for path, v := range T.Paths.Map() { + for method, opt := range v.Operations() { + apis = append(apis, &mcp_dto.API{ + Name: opt.Summary, + Method: method, + Path: path, + Description: opt.Description, + }) + } + } + result = append(result, &mcp_dto.Service{ + Id: s.Id, + Name: s.Name, + Description: s.Name, + ServiceKind: s.Kind.String(), + CreateTime: s.CreateTime, + UpdateTime: s.UpdateTime, + Apis: apis, + }) + } + data, _ := json.Marshal(result) + return mcp.NewToolResultText(string(data)), nil + +} + +func (i *imlMcpModule) Apps(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { + keyword := req.Params.Arguments["keyword"].(string) + condition := make(map[string]interface{}) + condition["as_app"] = true + list, err := i.serviceService.Search(ctx, keyword, condition, "update_at desc") + if err != nil { + return nil, fmt.Errorf("search service error: %w", err) + } + if len(list) == 0 { + list, err = i.serviceService.Search(ctx, "", condition, "update_at desc") + if err != nil { + return nil, fmt.Errorf("search service error: %w", err) + } + } + data, _ := json.Marshal(utils.SliceToSlice(list, func(s *service.Service) *mcp_dto.App { + return &mcp_dto.App{ + Id: s.Id, + Name: s.Name, + Description: s.Name, + CreateTime: s.CreateTime, + UpdateTime: s.UpdateTime, + } + })) + return mcp.NewToolResultText(string(data)), nil +} + +func (i *imlMcpModule) APIs(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { + serviceId, _ := req.Params.Arguments["service"].(string) + serviceIds := make([]string, 0, 1) + if serviceId == "" { + serviceIds = append(serviceIds, serviceId) + } + serviceList, err := i.serviceService.ServiceList(ctx) + if err != nil { + return nil, fmt.Errorf("get service list error: %w", err) + } + result := make([]*mcp_dto.ServiceAPI, 0, len(serviceList)) + + for _, s := range serviceList { + serviceRelease, err := i.releaseService.GetRunning(ctx, s.Id) + if err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + return nil, fmt.Errorf("get service release error: %w,service id is %s", err, s.Id) + } + continue + } + _, _, apiDocRelease, _, _, err := i.releaseService.GetReleaseInfos(ctx, serviceRelease.UUID) + if err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + return nil, fmt.Errorf("get service release info error: %w,service id is %s", err, s.Id) + } + continue + } + commit, err := i.apiDocService.GetDocCommit(ctx, apiDocRelease.Commit) + if err != nil { + if !errors.Is(err, gorm.ErrRecordNotFound) { + return nil, fmt.Errorf("get api doc release error: %w,service id is %s", err, s.Id) + } + continue + } + T, err := openapi3Loader.LoadFromData([]byte(commit.Data.Content)) + if err != nil { + return nil, fmt.Errorf("load openapi3 error: %w,service id is %s", err, s.Id) + } + result = append(result, &mcp_dto.ServiceAPI{ + ServiceID: s.Id, + ServiceName: s.Name, + APIDoc: T, + }) + } + data, _ := json.Marshal(result) + return mcp.NewToolResultText(string(data)), nil +} + +func (i *imlMcpModule) SubscriberAuthorizations(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { + serviceId, ok := req.Params.Arguments["service"].(string) + if !ok { + return nil, fmt.Errorf("service id is required") + } + subscribes, err := i.subscriberService.Subscribers(ctx, serviceId, subscribe.ApplyStatusSubscribe) + if err != nil { + return nil, fmt.Errorf("get subscriber error: %w,service id is %s", err, serviceId) + } + appIds := utils.SliceToSlice(subscribes, func(s *subscribe.Subscribe) string { + return s.Application + }) + if len(appIds) == 0 { + return nil, fmt.Errorf("no subscriber found,service id is %s", serviceId) + } + list, err := i.appAuthorizationService.ListByApp(ctx, appIds...) + if err != nil { + return nil, fmt.Errorf("get app authorization error: %w,app ids is %s", err, appIds) + } + result := utils.SliceToSlice(list, func(a *application_authorization.Authorization) *mcp_dto.AppAuthorization { + return &mcp_dto.AppAuthorization{ + Id: a.UUID, + Name: a.Name, + Position: a.Position, + TokenName: a.TokenName, + Config: a.Config, + } + }, func(a *application_authorization.Authorization) bool { + if a.Type != "apikey" { + return false + } + if a.ExpireTime != 0 && a.ExpireTime < time.Now().Unix() { + return false + } + return true + }) + data, _ := json.Marshal(result) + return mcp.NewToolResultText(string(data)), nil +} + +var ( + client = &http.Client{} +) + +func (i *imlMcpModule) Invoke(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) { + gatewayInvoke := utils.GatewayInvoke(ctx) + + if gatewayInvoke == "" { + return nil, fmt.Errorf("gateway invoke is required") + } + u, err := url.Parse(gatewayInvoke) + if err != nil { + return nil, fmt.Errorf("parse gateway invoke error: %w", err) + } + if u.Scheme == "" { + u.Scheme = "http" + } + + path, ok := req.Params.Arguments["path"].(string) + if !ok { + return nil, fmt.Errorf("invalid path") + } + u.Path = fmt.Sprintf("%s/%s", strings.TrimSuffix(u.Path, "/"), strings.TrimPrefix(path, "/")) + + method, ok := req.Params.Arguments["method"].(string) + if !ok { + method = "GET" + } + queryParam := url.Values{} + query, ok := req.Params.Arguments["query"].(map[string]interface{}) + if ok { + for k, v := range query { + switch v := v.(type) { + case string: + queryParam.Add(k, v) + case []string: + for _, value := range v { + queryParam.Add(k, value) + } + default: + return nil, fmt.Errorf("invalid query param type: %T", v) + } + } + } + u.RawQuery = queryParam.Encode() + headerParam := http.Header{} + header, ok := req.Params.Arguments["header"].(map[string]interface{}) + if ok { + for k, v := range header { + switch v := v.(type) { + case string: + headerParam.Set(k, v) + case []string: + for _, value := range v { + headerParam.Set(k, value) + } + default: + return nil, fmt.Errorf("invalid header param type: %T", v) + } + } + } + + body, ok := req.Params.Arguments["body"].(string) + if !ok { + body = "" + } + + contentType, ok := req.Params.Arguments["content-type"].(string) + if !ok { + contentType = "application/json" + } + request, err := http.NewRequest(method, u.String(), strings.NewReader(body)) + if err != nil { + return nil, fmt.Errorf("new request error: %w", err) + } + request.Header = headerParam + request.Header.Set("Content-Type", contentType) + apikey := utils.Label(ctx, "apikey") + if apikey != "" { + request.Header.Set("Authorization", utils.Md5(apikey)) + } + + resp, err := client.Do(request) + if err != nil { + return nil, fmt.Errorf("request error: %w", err) + } + defer resp.Body.Close() + + data, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("read response error: %w", err) + } + if resp.StatusCode != 200 { + return nil, fmt.Errorf("response error: %s", string(data)) + } + return mcp.NewToolResultText(string(data)), nil +} diff --git a/module/mcp/module.go b/module/mcp/module.go new file mode 100644 index 00000000..1549dc11 --- /dev/null +++ b/module/mcp/module.go @@ -0,0 +1,29 @@ +package mcp + +import ( + "context" + "reflect" + + "github.com/eolinker/go-common/autowire" + + "github.com/mark3labs/mcp-go/mcp" +) + +type IMcpModule interface { + // Services 获取服务列表 + Services(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) + // Apps 获取应用列表 + Apps(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) + // APIs 获取API列表 + APIs(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) + + // SubscriberAuthorizations 获取订阅者授权 + SubscriberAuthorizations(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) + Invoke(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) +} + +func init() { + autowire.Auto[IMcpModule](func() reflect.Value { + return reflect.ValueOf(new(imlMcpModule)) + }) +} diff --git a/module/monitor/iml.go b/module/monitor/iml.go index 6e497a49..63d127e4 100644 --- a/module/monitor/iml.go +++ b/module/monitor/iml.go @@ -596,9 +596,9 @@ func (i *imlMonitorStatisticModule) TopAPIStatistics(ctx context.Context, limit } else { statisticItem.IsRed = true if key == "-" { - statisticItem.Name = "无API" + statisticItem.Name = "Unknown API" } else { - statisticItem.Name = fmt.Sprintf("未知API-%s", key) + statisticItem.Name = fmt.Sprintf("Unknow-%s", key) } } result = append(result, statisticItem) diff --git a/module/publish/iml.go b/module/publish/iml.go index 4226ef1f..692a5f3a 100644 --- a/module/publish/iml.go +++ b/module/publish/iml.go @@ -7,6 +7,11 @@ import ( "fmt" "time" + mcp_server "github.com/APIParkLab/APIPark/mcp-server" + api_doc "github.com/APIParkLab/APIPark/service/api-doc" + "github.com/mark3labs/mcp-go/mcp" + "github.com/mitchellh/mapstructure" + strategy_driver "github.com/APIParkLab/APIPark/module/strategy/driver" strategy_dto "github.com/APIParkLab/APIPark/module/strategy/dto" @@ -50,6 +55,7 @@ type imlPublishModule struct { releaseModule releaseModule.IReleaseModule `autowired:""` publishService publish.IPublishService `autowired:""` apiService api.IAPIService `autowired:""` + apiDocService api_doc.IAPIDocService `autowired:""` upstreamService upstream.IUpstreamService `autowired:""` strategyService strategy.IStrategyService `autowired:""` releaseService release.IReleaseService `autowired:""` @@ -515,24 +521,39 @@ func (m *imlPublishModule) Publish(ctx context.Context, serviceId string, id str return err } hasError := false - - for _, c := range clusters { - err = m.publish(ctx, flow.Id, c.Uuid, projectReleaseMap[c.Uuid]) - if err != nil { - hasError = true - log.Error(err) - continue + return m.transaction.Transaction(ctx, func(ctx context.Context) error { + for _, c := range clusters { + err = m.publish(ctx, flow.Id, c.Uuid, projectReleaseMap[c.Uuid]) + if err != nil { + hasError = true + log.Error(err) + continue + } } - } - err = m.releaseService.SetRunning(ctx, serviceId, flow.Release) - if err != nil { - return err - } - status := publish.StatusDone - if hasError { - status = publish.StatusPublishError - } - return m.publishService.SetStatus(ctx, serviceId, id, status) + err = m.releaseService.SetRunning(ctx, serviceId, flow.Release) + if err != nil { + return err + } + status := publish.StatusDone + if hasError { + status = publish.StatusPublishError + } + if status == publish.StatusDone { + info, err := m.serviceService.Get(ctx, serviceId) + if err != nil { + return err + } + if info.EnableMCP { + err = m.updateMCPServer(ctx, serviceId, info.Name, flow.Version) + if err != nil { + return err + } + } + } + + return m.publishService.SetStatus(ctx, serviceId, id, status) + }) + } func (m *imlPublishModule) List(ctx context.Context, serviceId string, page, pageSize int) ([]*dto.Publish, int64, error) { @@ -550,6 +571,81 @@ func (m *imlPublishModule) List(ctx context.Context, serviceId string, page, pag }), total, nil } +func (i *imlPublishModule) updateMCPServer(ctx context.Context, sid string, name string, version string) error { + r, err := i.releaseService.GetRunning(ctx, sid) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil + } + return err + } + _, _, apiDocCommit, _, _, err := i.releaseService.GetReleaseInfos(ctx, r.UUID) + if err != nil { + return fmt.Errorf("get release info error: %w", err) + } + commitDoc, err := i.apiDocService.GetDocCommit(ctx, apiDocCommit.Commit) + if err != nil { + return fmt.Errorf("get api doc commit error: %w", err) + } + mcpInfo, err := mcp_server.ConvertMCPFromOpenAPI3Data([]byte(commitDoc.Data.Content)) + if err != nil { + return fmt.Errorf("convert mcp from openapi3 data error: %w", err) + } + tools := make([]mcp_server.ITool, 0, len(mcpInfo.Apis)) + for _, a := range mcpInfo.Apis { + toolOptions := make([]mcp.ToolOption, 0, len(a.Params)+2) + toolOptions = append(toolOptions, mcp.WithDescription(a.Description)) + headers := make(map[string]interface{}) + queries := make(map[string]interface{}) + path := make(map[string]interface{}) + for _, v := range a.Params { + p := map[string]interface{}{ + "type": "string", + "required": v.Required, + "description": v.Description, + } + switch v.In { + case "header": + headers[v.Name] = p + case "query": + queries[v.Name] = p + case "path": + path[v.Name] = p + } + } + if len(headers) > 0 { + toolOptions = append(toolOptions, mcp.WithObject(mcp_server.MCPHeader, mcp.Properties(headers), mcp.Description("request headers."))) + } + if len(queries) > 0 { + toolOptions = append(toolOptions, mcp.WithObject(mcp_server.MCPQuery, mcp.Properties(queries), mcp.Description("request queries."))) + } + if len(path) > 0 { + toolOptions = append(toolOptions, mcp.WithObject(mcp_server.MCPPath, mcp.Properties(path), mcp.Description("request path params."))) + } + if a.Body != nil { + type Schema struct { + Type string `mapstructure:"type"` + Properties map[string]interface{} `mapstructure:"properties"` + Items interface{} `mapstructure:"items"` + } + var tmp Schema + err = mapstructure.Decode(a.Body, &tmp) + if err != nil { + return err + } + switch tmp.Type { + case "object": + toolOptions = append(toolOptions, mcp.WithObject(mcp_server.MCPBody, mcp.Properties(tmp.Properties), mcp.Description("request body,it is avalible when method is POST、PUT、PATCH."))) + case "array": + toolOptions = append(toolOptions, mcp.WithArray(mcp_server.MCPBody, mcp.Items(tmp.Items), mcp.Description("request body,it is avalible when method is POST、PUT、PATCH."))) + } + } + tools = append(tools, mcp_server.NewTool(a.Summary, a.Path, a.Method, a.ContentType, toolOptions...)) + } + mcp_server.SetSSEServer(sid, name, version, tools...) + return nil +} + func (m *imlPublishModule) Detail(ctx context.Context, serviceId string, id string) (*dto.PublishDetail, error) { _, err := m.serviceService.Check(ctx, serviceId, asServer) if err != nil { diff --git a/module/router/dto/input.go b/module/router/dto/input.go index 593a3647..41198b00 100644 --- a/module/router/dto/input.go +++ b/module/router/dto/input.go @@ -63,6 +63,7 @@ func (a *Create) Validate() error { } type Edit struct { + Name *string `json:"name"` Description *string `json:"description"` Proxy *InputProxy `json:"proxy"` Path *string `json:"path"` diff --git a/module/router/iml.go b/module/router/iml.go index e810c0e5..7cac0566 100644 --- a/module/router/iml.go +++ b/module/router/iml.go @@ -324,6 +324,7 @@ func (i *imlRouterModule) Edit(ctx context.Context, serviceId string, apiId stri match = &m } err = i.apiService.Save(ctx, apiId, &api.Edit{ + Name: dto.Name, Description: dto.Description, Methods: dto.Methods, Protocols: dto.Protocols, diff --git a/module/service/dto/input.go b/module/service/dto/input.go index 7569d1b5..bdaa7bc5 100644 --- a/module/service/dto/input.go +++ b/module/service/dto/input.go @@ -24,6 +24,7 @@ type CreateService struct { AsApp *bool `json:"as_app"` AsServer *bool `json:"as_server"` ModelMapping string `json:"model_mapping"` + EnableMCP bool `json:"enable_mcp"` } type EditService struct { @@ -37,7 +38,8 @@ type EditService struct { Model *string `json:"model"` ApprovalType *string `json:"approval_type"` State *string `json:"state"` - ModelMapping string `json:"model_mapping"` + ModelMapping *string `json:"model_mapping"` + EnableMCP *bool `json:"enable_mcp"` } type CreateApp struct { diff --git a/module/service/dto/output.go b/module/service/dto/output.go index 636420f0..35a80cbe 100644 --- a/module/service/dto/output.go +++ b/module/service/dto/output.go @@ -1,6 +1,7 @@ package service_dto import ( + ai_provider_local "github.com/APIParkLab/APIPark/ai-provider/local" "github.com/APIParkLab/APIPark/service/service" "github.com/eolinker/go-common/auto" ) @@ -55,6 +56,7 @@ type ServiceItem struct { Provider *auto.Label `json:"provider,omitempty" aolabel:"ai_provider"` State string `json:"state"` CanDelete bool `json:"can_delete"` + EnableMCP bool `json:"enable_mcp"` } type AppItem struct { @@ -100,11 +102,12 @@ type Service struct { ProviderType string `json:"provider_type,omitempty"` Model string `json:"model,omitempty"` ApprovalType string `json:"approval_type"` - AsServer bool `json:"as_server"` - AsApp bool `json:"as_app"` ServiceKind string `json:"service_kind"` State string `json:"state"` ModelMapping string `json:"model_mapping"` + AsServer bool `json:"as_server"` + AsApp bool `json:"as_app"` + EnableMCP bool `json:"enable_mcp"` } type App struct { @@ -138,6 +141,7 @@ func ToService(model *service.Service) *Service { AsServer: model.AsServer, AsApp: model.AsApp, ServiceKind: model.Kind.String(), + EnableMCP: model.EnableMCP, } state := FromServiceState(model.State) if state == ServiceStateNormal { @@ -151,7 +155,7 @@ func ToService(model *service.Service) *Service { provider := auto.UUID(model.AdditionalConfig["provider"]) s.Provider = &provider s.ProviderType = "local" - if provider.Id != "ollama" { + if provider.Id != ai_provider_local.ProviderLocal { s.ProviderType = "online" } modelId := model.AdditionalConfig["model"] diff --git a/module/service/iml.go b/module/service/iml.go index f081fc86..72d233dd 100644 --- a/module/service/iml.go +++ b/module/service/iml.go @@ -2,19 +2,33 @@ package service import ( "context" + "encoding/json" "errors" "fmt" "sort" "strings" "time" + "github.com/mitchellh/mapstructure" + + "github.com/eolinker/go-common/register" + + "github.com/mark3labs/mcp-go/mcp" + + mcp_server "github.com/APIParkLab/APIPark/mcp-server" + + "github.com/APIParkLab/APIPark/service/release" + + "github.com/APIParkLab/APIPark/gateway" + ai_local "github.com/APIParkLab/APIPark/service/ai-local" + "github.com/APIParkLab/APIPark/service/cluster" model_runtime "github.com/APIParkLab/APIPark/ai-provider/model-runtime" - "github.com/eolinker/eosc/log" - "github.com/APIParkLab/APIPark/resources/access" + "github.com/eolinker/eosc/log" + "github.com/eolinker/go-common/server" "github.com/eolinker/ap-account/service/role" @@ -68,11 +82,149 @@ type imlServiceModule struct { serviceTagService service_tag.ITagService `autowired:""` apiService api.IAPIService `autowired:""` apiDocService api_doc.IAPIDocService `autowired:""` + clusterService cluster.IClusterService `autowired:""` transaction store.ITransaction `autowired:""` + releaseService release.IReleaseService `autowired:""` serviceModelMappingService service_model_mapping.IServiceModelMappingService `autowired:""` } +func (i *imlServiceModule) OnInit() { + register.Handle(func(v server.Server) { + ctx := context.Background() + services, err := i.serviceService.ServiceList(ctx) + if err != nil { + log.Error(err) + return + } + for _, s := range services { + err = i.updateMCPServer(ctx, s.Id, s.Name, "1.0") + if err != nil { + log.Error(err) + return + } + } + }) +} + +func (i *imlServiceModule) initGateway(ctx context.Context, clusterId string, clientDriver gateway.IClientDriver) error { + services, err := i.serviceService.ServiceList(ctx) + if err != nil { + return err + } + subscribeReleases := make([]*gateway.SubscribeRelease, 0, len(services)) + hashReleases := make([]*gateway.HashRelease, 0, len(services)) + for _, s := range services { + subscribeReleases = append(subscribeReleases, &gateway.SubscribeRelease{ + Service: s.Id, + Application: "apipark-global", + Expired: "0", + }) + + modelMap, err := i.serviceModelMappingService.Get(ctx, s.Id) + if err != nil { + return err + } + if modelMap.Content == "" { + continue + } + m := make(map[string]string) + err = json.Unmarshal([]byte(modelMap.Content), &m) + if err != nil { + return err + } + hashReleases = append(hashReleases, &gateway.HashRelease{ + HashKey: fmt.Sprintf("%s:%s", gateway.KeyServiceMapping, s.Id), + HashMap: m, + }) + + } + err = clientDriver.Subscribe().Online(ctx, subscribeReleases...) + if err != nil { + return err + } + return clientDriver.Hash().Online(ctx, hashReleases...) +} + +func (i *imlServiceModule) updateMCPServer(ctx context.Context, sid string, name string, version string) error { + r, err := i.releaseService.GetRunning(ctx, sid) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil + } + return err + } + _, _, apiDocCommit, _, _, err := i.releaseService.GetReleaseInfos(ctx, r.UUID) + if err != nil { + return fmt.Errorf("get release info error: %w", err) + } + commitDoc, err := i.apiDocService.GetDocCommit(ctx, apiDocCommit.Commit) + if err != nil { + return fmt.Errorf("get api doc commit error: %w", err) + } + mcpInfo, err := mcp_server.ConvertMCPFromOpenAPI3Data([]byte(commitDoc.Data.Content)) + if err != nil { + return fmt.Errorf("convert mcp from openapi3 data error: %w", err) + } + tools := make([]mcp_server.ITool, 0, len(mcpInfo.Apis)) + for _, a := range mcpInfo.Apis { + toolOptions := make([]mcp.ToolOption, 0, len(a.Params)+2) + toolOptions = append(toolOptions, mcp.WithDescription(a.Description)) + headers := make(map[string]interface{}) + queries := make(map[string]interface{}) + path := make(map[string]interface{}) + for _, v := range a.Params { + p := map[string]interface{}{ + "type": "string", + "required": v.Required, + "description": v.Description, + } + switch v.In { + case "header": + headers[v.Name] = p + case "query": + queries[v.Name] = p + case "path": + path[v.Name] = p + } + } + if len(headers) > 0 { + toolOptions = append(toolOptions, mcp.WithObject(mcp_server.MCPHeader, mcp.Properties(headers), mcp.Description("request headers."))) + } + if len(queries) > 0 { + toolOptions = append(toolOptions, mcp.WithObject(mcp_server.MCPQuery, mcp.Properties(queries), mcp.Description("request queries."))) + } + if len(path) > 0 { + toolOptions = append(toolOptions, mcp.WithObject(mcp_server.MCPPath, mcp.Properties(path), mcp.Description("request path params."))) + } + if a.Body != nil { + type Schema struct { + Type string `mapstructure:"type"` + Properties map[string]interface{} `mapstructure:"properties"` + Items interface{} `mapstructure:"items"` + } + var tmp Schema + err = mapstructure.Decode(a.Body, &tmp) + if err != nil { + return err + } + switch tmp.Type { + case "object": + toolOptions = append(toolOptions, mcp.WithObject(mcp_server.MCPBody, mcp.Properties(tmp.Properties), mcp.Description("request body,it is avalible when method is POST、PUT、PATCH."))) + case "array": + toolOptions = append(toolOptions, mcp.WithArray(mcp_server.MCPBody, mcp.Items(tmp.Items), mcp.Description("request body,it is avalible when method is POST、PUT、PATCH."))) + } + } + tools = append(tools, mcp_server.NewTool(a.Summary, a.Path, a.Method, a.ContentType, toolOptions...)) + } + mcp_server.SetSSEServer(sid, name, version, tools...) + return nil +} + +func (i *imlServiceModule) deleteMCPServer(ctx context.Context, sid string) { + mcp_server.DelSSEServer(sid) +} + func (i *imlServiceModule) ExportAll(ctx context.Context) ([]*service_dto.ExportService, error) { services, err := i.serviceService.ServiceList(ctx) if err != nil { @@ -299,6 +451,7 @@ func toServiceItem(model *service.Service) *service_dto.ServiceItem { CreateTime: auto.TimeLabel(model.CreateTime), UpdateTime: auto.TimeLabel(model.UpdateTime), Team: auto.UUID(model.Team), + EnableMCP: model.EnableMCP, ServiceKind: model.Kind.String(), } state := service_dto.FromServiceState(model.State) @@ -345,6 +498,7 @@ func (i *imlServiceModule) Create(ctx context.Context, teamID string, input *ser ApprovalType: service.ApprovalType(input.ApprovalType), AdditionalConfig: make(map[string]string), Kind: service.Kind(input.Kind), + EnableMCP: input.EnableMCP, } if mo.ServiceType == service.PublicService && mo.Catalogue == "" { return nil, fmt.Errorf("catalogue can not be empty") @@ -390,15 +544,54 @@ func (i *imlServiceModule) Create(ctx context.Context, teamID string, input *ser } } } - - err := i.serviceModelMappingService.Save(ctx, &service_model_mapping.Save{ - Sid: input.Id, - Content: input.ModelMapping, + err := i.serviceService.Create(ctx, mo) + if err != nil { + return err + } + client, err := i.clusterService.GatewayClient(ctx, cluster.DefaultClusterID) + if err != nil { + return err + } + err = client.Subscribe().Online(ctx, &gateway.SubscribeRelease{ + Service: mo.Id, + Application: "apipark-global", + Expired: "0", }) if err != nil { return err } - return i.serviceService.Create(ctx, mo) + if input.ModelMapping != "" { + m := make(map[string]string) + err = json.Unmarshal([]byte(input.ModelMapping), &m) + if err != nil { + return err + } + err = i.serviceModelMappingService.Save(ctx, &service_model_mapping.Save{ + Sid: input.Id, + Content: input.ModelMapping, + }) + if err != nil { + return err + } + + err = client.Hash().Online(ctx, &gateway.HashRelease{ + HashKey: fmt.Sprintf("%s:%s", gateway.KeyServiceMapping, input.Id), + HashMap: m, + }) + if err != nil { + return err + } + } + + if input.EnableMCP { + err = i.updateMCPServer(ctx, input.Id, input.Name, "1.0") + if err != nil { + return err + } + } else { + i.deleteMCPServer(ctx, input.Id) + } + return nil }) if err != nil { return nil, err @@ -440,6 +633,7 @@ func (i *imlServiceModule) Edit(ctx context.Context, id string, input *service_d Catalogue: input.Catalogue, AdditionalConfig: &info.AdditionalConfig, ApprovalType: &approvalType, + EnableMCP: input.EnableMCP, } if input.State != nil { state := service_dto.ServiceState(*input.State).Int() @@ -464,15 +658,59 @@ func (i *imlServiceModule) Edit(ctx context.Context, id string, input *service_d if err != nil { return err } + } } - err = i.serviceModelMappingService.Save(ctx, &service_model_mapping.Save{ - Sid: id, - Content: input.ModelMapping, + client, err := i.clusterService.GatewayClient(ctx, cluster.DefaultClusterID) + if err != nil { + return err + } + err = client.Subscribe().Online(ctx, &gateway.SubscribeRelease{ + Service: id, + Application: "apipark-global", + Expired: "0", }) if err != nil { return err } + if input.ModelMapping != nil && *input.ModelMapping != "" { + m := make(map[string]string) + err = json.Unmarshal([]byte(*input.ModelMapping), &m) + if err != nil { + return err + } + err = i.serviceModelMappingService.Save(ctx, &service_model_mapping.Save{ + Sid: id, + Content: *input.ModelMapping, + }) + if err != nil { + return err + } + + err = client.Hash().Online(ctx, &gateway.HashRelease{ + HashKey: fmt.Sprintf("%s:%s", gateway.KeyServiceMapping, id), + HashMap: m, + }) + if err != nil { + return err + } + } + + if input.EnableMCP != nil { + if *input.EnableMCP { + name := info.Name + if input.Name != nil { + name = *input.Name + } + err = i.updateMCPServer(ctx, id, name, "1.0") + if err != nil { + return err + } + } else { + i.deleteMCPServer(ctx, id) + } + } + return nil }) if err != nil { @@ -491,7 +729,36 @@ func (i *imlServiceModule) Delete(ctx context.Context, id string) error { return fmt.Errorf("service has apis, can not delete") } - return i.serviceService.Delete(ctx, id) + err = i.serviceService.Delete(ctx, id) + if err != nil { + return err + } + + err = i.serviceModelMappingService.Delete(ctx, id) + if err != nil { + return err + } + client, err := i.clusterService.GatewayClient(ctx, cluster.DefaultClusterID) + if err != nil { + return err + } + err = client.Subscribe().Offline(ctx, &gateway.SubscribeRelease{ + Service: id, + Application: "apipark-global", + Expired: "0", + }) + if err != nil { + return err + } + + err = client.Hash().Offline(ctx, &gateway.HashRelease{ + HashKey: fmt.Sprintf("%s:%s", gateway.KeyServiceMapping, id), + }) + if err != nil { + return err + } + i.deleteMCPServer(ctx, id) + return nil }) return err } diff --git a/module/service/module.go b/module/service/module.go index bbcc749f..93fbcbf1 100644 --- a/module/service/module.go +++ b/module/service/module.go @@ -4,6 +4,8 @@ import ( "context" "reflect" + "github.com/APIParkLab/APIPark/gateway" + strategy_filter "github.com/APIParkLab/APIPark/strategy-filter" "github.com/APIParkLab/APIPark/module/system" @@ -64,6 +66,7 @@ type IExportAppModule interface { func init() { serviceModule := new(imlServiceModule) autowire.Auto[IServiceModule](func() reflect.Value { + gateway.RegisterInitHandleFunc(serviceModule.initGateway) return reflect.ValueOf(serviceModule) }) diff --git a/module/system-apikey/dto/input.go b/module/system-apikey/dto/input.go new file mode 100644 index 00000000..f5c68b87 --- /dev/null +++ b/module/system-apikey/dto/input.go @@ -0,0 +1,14 @@ +package system_apikey_dto + +type Create struct { + Id string `json:"id"` + Name string `json:"name"` + Value string `json:"value"` + Expired int64 `json:"expired"` +} + +type Update struct { + Name *string `json:"name"` + Value *string `json:"value"` + Expired *int64 `json:"expired"` +} diff --git a/module/system-apikey/dto/output.go b/module/system-apikey/dto/output.go new file mode 100644 index 00000000..8ce57917 --- /dev/null +++ b/module/system-apikey/dto/output.go @@ -0,0 +1,50 @@ +package system_apikey_dto + +import ( + system_apikey "github.com/APIParkLab/APIPark/service/system-apikey" + "github.com/eolinker/go-common/auto" +) + +type APIKey struct { + *Item +} + +type Item struct { + *SimpleItem + Creator auto.Label `json:"creator" aolabel:"user"` + Updater auto.Label `json:"updater" aolabel:"user"` + CreateAt auto.TimeLabel `json:"create_time"` + UpdateAt auto.TimeLabel `json:"update_time"` +} + +type SimpleItem struct { + Id string `json:"id"` + Name string `json:"name"` + Value string `json:"value"` + Expired int64 `json:"expired"` +} + +func ToAPIKey(e *system_apikey.APIKey) *APIKey { + return &APIKey{ + Item: ToAPIKeyItem(e), + } +} + +func ToAPIKeySimpleItem(e *system_apikey.APIKey) *SimpleItem { + return &SimpleItem{ + Id: e.Id, + Name: e.Name, + Value: e.Value, + Expired: e.Expired, + } +} + +func ToAPIKeyItem(e *system_apikey.APIKey) *Item { + return &Item{ + SimpleItem: ToAPIKeySimpleItem(e), + Creator: auto.UUID(e.Creator), + Updater: auto.UUID(e.Updater), + CreateAt: auto.TimeLabel(e.CreateAt), + UpdateAt: auto.TimeLabel(e.UpdateAt), + } +} diff --git a/module/system-apikey/iml.go b/module/system-apikey/iml.go new file mode 100644 index 00000000..3d778952 --- /dev/null +++ b/module/system-apikey/iml.go @@ -0,0 +1,204 @@ +package system_apikey + +import ( + "context" + "encoding/json" + "time" + + application_authorization "github.com/APIParkLab/APIPark/service/application-authorization" + + "github.com/APIParkLab/APIPark/service/service" + + "github.com/APIParkLab/APIPark/service/cluster" + + team_member "github.com/APIParkLab/APIPark/service/team-member" + + "github.com/eolinker/go-common/store" + + "github.com/APIParkLab/APIPark/gateway" + + "github.com/eolinker/go-common/utils" + + "github.com/google/uuid" + + system_apikey "github.com/APIParkLab/APIPark/service/system-apikey" + + system_apikey_dto "github.com/APIParkLab/APIPark/module/system-apikey/dto" +) + +var _ IAPIKeyModule = new(imlAPIKeyModule) + +type imlAPIKeyModule struct { + apikeyService system_apikey.IAPIKeyService `autowired:""` + clusterServer cluster.IClusterService `autowired:""` + teamMemberService team_member.ITeamMemberService `autowired:""` + serviceService service.IServiceService `autowired:""` + applicationAuthorizationService application_authorization.IAuthorizationService `autowired:""` + transaction store.ITransaction `autowired:""` +} + +func (i *imlAPIKeyModule) MyAPIKeys(ctx context.Context) ([]*system_apikey_dto.SimpleItem, error) { + members, err := i.teamMemberService.Members(ctx, nil, nil) + if err != nil { + return nil, err + } + if len(members) == 0 { + return nil, nil + } + teamIds := utils.SliceToSlice(members, func(m *team_member.Member) string { + return m.Come + }) + apps, err := i.serviceService.AppListByTeam(ctx, teamIds...) + if err != nil { + return nil, err + } + appIds := utils.SliceToSlice(apps, func(a *service.Service) string { + return a.Id + }) + auths, err := i.applicationAuthorizationService.ListByApp(ctx, appIds...) + if err != nil { + return nil, err + } + result := make([]*system_apikey_dto.SimpleItem, 0, len(auths)) + for _, a := range auths { + if a.Type != "apikey" { + continue + } + m := make(map[string]string) + json.Unmarshal([]byte(a.Config), &m) + if m["apikey"] == "" { + continue + } + result = append(result, &system_apikey_dto.SimpleItem{ + Id: a.UUID, + Name: a.Name, + Value: m["apikey"], + Expired: a.ExpireTime, + }) + + } + return result, nil + +} + +func (i *imlAPIKeyModule) Create(ctx context.Context, input *system_apikey_dto.Create) error { + if input.Id == "" { + input.Id = uuid.NewString() + } + return i.transaction.Transaction(ctx, func(ctx context.Context) error { + err := i.apikeyService.Create(ctx, &system_apikey.Create{ + Id: input.Id, + Name: input.Name, + Value: input.Value, + Expired: input.Expired, + }) + if err != nil { + return err + } + client, err := i.clusterServer.GatewayClient(ctx, cluster.DefaultClusterID) + if err != nil { + return err + } + return i.online(ctx, client) + }) +} + +func (i *imlAPIKeyModule) Update(ctx context.Context, id string, input *system_apikey_dto.Update) error { + return i.transaction.Transaction(ctx, func(ctx context.Context) error { + err := i.apikeyService.Save(ctx, id, &system_apikey.Update{ + Name: input.Name, + Value: input.Value, + Expired: input.Expired, + }) + if err != nil { + return err + } + client, err := i.clusterServer.GatewayClient(ctx, cluster.DefaultClusterID) + if err != nil { + return err + } + return i.online(ctx, client) + }) +} + +func (i *imlAPIKeyModule) Delete(ctx context.Context, id string) error { + return i.transaction.Transaction(ctx, func(ctx context.Context) error { + err := i.apikeyService.Delete(ctx, id) + if err != nil { + return err + } + client, err := i.clusterServer.GatewayClient(ctx, cluster.DefaultClusterID) + if err != nil { + return err + } + return i.online(ctx, client) + }) +} + +func (i *imlAPIKeyModule) Get(ctx context.Context, id string) (*system_apikey_dto.APIKey, error) { + info, err := i.apikeyService.Get(ctx, id) + if err != nil { + return nil, err + } + return system_apikey_dto.ToAPIKey(info), nil +} + +func (i *imlAPIKeyModule) Search(ctx context.Context, keyword string) ([]*system_apikey_dto.Item, error) { + list, err := i.apikeyService.Search(ctx, keyword, nil, "create_at desc") + if err != nil { + return nil, err + } + + return utils.SliceToSlice(list, system_apikey_dto.ToAPIKeyItem), nil +} + +func (i *imlAPIKeyModule) SimpleList(ctx context.Context) ([]*system_apikey_dto.SimpleItem, error) { + list, err := i.apikeyService.Search(ctx, "", nil, "create_at desc") + if err != nil { + return nil, err + } + + return utils.SliceToSlice(list, system_apikey_dto.ToAPIKeySimpleItem), nil +} + +func (i *imlAPIKeyModule) online(ctx context.Context, client gateway.IClientDriver) error { + + // 获取所有apikey + list, err := i.apikeyService.Search(ctx, "", nil, "create_at desc") + if err != nil { + return err + } + app := &gateway.ApplicationRelease{ + BasicItem: &gateway.BasicItem{ + ID: "apipark-global", + Description: "apipark global consumer", + Version: time.Now().Format("20060102150405"), + }, + Authorizations: utils.SliceToSlice(list, func(a *system_apikey.APIKey) *gateway.Authorization { + authCfg := map[string]interface{}{ + "apikey": utils.Md5(a.Value), + } + return &gateway.Authorization{ + Type: "apikey", + Position: "header", + TokenName: "Authorization", + Expire: a.Expired, + Config: authCfg, + HideCredential: true, + Label: map[string]string{ + "authorization": a.Id, + "authorization_name": a.Name, + }, + } + }), + } + err = client.Application().Online(ctx, app) + if err != nil { + return err + } + return nil +} + +func (i *imlAPIKeyModule) initGateway(ctx context.Context, clusterId string, clientDriver gateway.IClientDriver) error { + return i.online(ctx, clientDriver) +} diff --git a/module/system-apikey/module.go b/module/system-apikey/module.go new file mode 100644 index 00000000..f80e2a4d --- /dev/null +++ b/module/system-apikey/module.go @@ -0,0 +1,29 @@ +package system_apikey + +import ( + "context" + "reflect" + + "github.com/eolinker/go-common/autowire" + + "github.com/APIParkLab/APIPark/gateway" + system_apikey_dto "github.com/APIParkLab/APIPark/module/system-apikey/dto" +) + +type IAPIKeyModule interface { + Create(ctx context.Context, input *system_apikey_dto.Create) error + Update(ctx context.Context, id string, input *system_apikey_dto.Update) error + Delete(ctx context.Context, id string) error + Get(ctx context.Context, id string) (*system_apikey_dto.APIKey, error) + Search(ctx context.Context, keyword string) ([]*system_apikey_dto.Item, error) + SimpleList(ctx context.Context) ([]*system_apikey_dto.SimpleItem, error) + MyAPIKeys(ctx context.Context) ([]*system_apikey_dto.SimpleItem, error) +} + +func init() { + apikeyModule := new(imlAPIKeyModule) + autowire.Auto[IAPIKeyModule](func() reflect.Value { + gateway.RegisterInitHandleFunc(apikeyModule.initGateway) + return reflect.ValueOf(apikeyModule) + }) +} diff --git a/module/system/iml.go b/module/system/iml.go index b74a231b..20e6b6b5 100644 --- a/module/system/iml.go +++ b/module/system/iml.go @@ -49,7 +49,7 @@ func (i *imlSettingModule) Set(ctx context.Context, input *system_dto.InputSetti } } if input.OllamaAddress != nil { - ai_provider_local.ResetOllamaAddress(*input.OllamaAddress) + ai_provider_local.ResetLocalAddress(*input.OllamaAddress) } return nil }) @@ -61,7 +61,7 @@ func (i *imlSettingModule) OnInit() { address, has := i.settingService.Get(ctx, "system.ai_model.ollama_address") if has { - ai_provider_local.ResetOllamaAddress(address) + ai_provider_local.ResetLocalAddress(address) } }) diff --git a/plugins/core/core.go b/plugins/core/core.go index b3afb4b1..761e4913 100644 --- a/plugins/core/core.go +++ b/plugins/core/core.go @@ -1,9 +1,14 @@ package core import ( - ai_model "github.com/APIParkLab/APIPark/controller/ai-model" "net/http" + system_apikey "github.com/APIParkLab/APIPark/controller/system-apikey" + + "github.com/APIParkLab/APIPark/controller/mcp" + + ai_model "github.com/APIParkLab/APIPark/controller/ai-model" + ai_balance "github.com/APIParkLab/APIPark/controller/ai-balance" ai_local "github.com/APIParkLab/APIPark/controller/ai-local" @@ -102,6 +107,8 @@ type plugin struct { settingController system.ISettingController `autowired:""` initController system.IInitController `autowired:""` logController log.ILogController `autowired:""` + mcpController mcp.IMcpController `autowired:""` + systemAPIKeyController system_apikey.IAPIKeyController `autowired:""` apis []pm3.Api } @@ -128,6 +135,8 @@ func (p *plugin) OnComplete() { p.apis = append(p.apis, p.logApis()...) p.apis = append(p.apis, p.aiLocalApis()...) p.apis = append(p.apis, p.aiBalanceAPIs()...) + p.apis = append(p.apis, p.mcpAPIs()...) + p.apis = append(p.apis, p.systemApikeyApis()...) } func (p *plugin) Name() string { diff --git a/plugins/core/mcp.go b/plugins/core/mcp.go new file mode 100644 index 00000000..4e29c42e --- /dev/null +++ b/plugins/core/mcp.go @@ -0,0 +1,29 @@ +package core + +import ( + "fmt" + "net/http" + + mcp_server "github.com/APIParkLab/APIPark/mcp-server" + "github.com/eolinker/go-common/ignore" + + "github.com/eolinker/go-common/pm3" +) + +func (p *plugin) mcpAPIs() []pm3.Api { + serviceSSEPath := fmt.Sprintf("/api/v1/%s/:serviceId/sse", mcp_server.ServiceBasePath) + serviceMessagePath := fmt.Sprintf("/api/v1/%s/:serviceId/message", mcp_server.ServiceBasePath) + globalSSEPath := fmt.Sprintf("/api/v1/%s/sse", mcp_server.GlobalBasePath) + globalMessagePath := fmt.Sprintf("/api/v1/%s/message", mcp_server.GlobalBasePath) + ignore.IgnorePath("login", http.MethodGet, serviceSSEPath) + ignore.IgnorePath("login", http.MethodPost, serviceMessagePath) + ignore.IgnorePath("login", http.MethodGet, globalSSEPath) + ignore.IgnorePath("login", http.MethodPost, globalMessagePath) + return []pm3.Api{ + pm3.CreateApiSimple(http.MethodGet, serviceSSEPath, p.mcpController.MCPHandle), + pm3.CreateApiSimple(http.MethodPost, serviceMessagePath, p.mcpController.MCPHandle), + pm3.CreateApiSimple(http.MethodGet, globalSSEPath, p.mcpController.GlobalMCPHandle), + pm3.CreateApiSimple(http.MethodPost, globalMessagePath, p.mcpController.GlobalMCPHandle), + pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/global/mcp/config", []string{"context"}, []string{"config"}, p.mcpController.GlobalMCPConfig), + } +} diff --git a/plugins/core/service.go b/plugins/core/service.go index 82ac2107..60a48a69 100644 --- a/plugins/core/service.go +++ b/plugins/core/service.go @@ -11,6 +11,7 @@ import ( func (p *plugin) ServiceApis() []pm3.Api { ignore.IgnorePath("login", http.MethodGet, "/api/v1/service/swagger/:id") + ignore.IgnorePath("login", http.MethodGet, "/api/v1/service/apidoc/:id") return []pm3.Api{ // 项目 pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/service/info", []string{"context", "query:service"}, []string{"service"}, p.serviceController.Get, access.SystemWorkspaceServiceViewAll, access.TeamTeamServiceView), @@ -36,6 +37,7 @@ func (p *plugin) ServiceApis() []pm3.Api { pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/service/doc", []string{"context", "query:service"}, []string{"doc"}, p.serviceController.ServiceDoc, access.SystemWorkspaceServiceViewAll, access.TeamServiceServiceIntroView), pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/service/doc", []string{"context", "query:service", "body"}, nil, p.serviceController.SaveServiceDoc, access.SystemWorkspaceServiceManagerAll, access.TeamServiceServiceIntroManager), pm3.CreateApiSimple(http.MethodGet, "/api/v1/service/swagger/:id", p.serviceController.Swagger), + pm3.CreateApiSimple(http.MethodGet, "/api/v1/service/apidoc/:id", p.serviceController.Swagger), pm3.CreateApiSimple(http.MethodGet, "/api/v1/export/openapi/:id", p.serviceController.ExportSwagger), } } diff --git a/plugins/core/system.go b/plugins/core/system.go index a44a921c..669ac648 100644 --- a/plugins/core/system.go +++ b/plugins/core/system.go @@ -15,3 +15,15 @@ func (p *plugin) systemApis() []pm3.Api { pm3.CreateApiWidthDoc(http.MethodPost, "/api/v1/system/general", []string{"context", "body"}, nil, p.settingController.Set), } } + +func (p *plugin) systemApikeyApis() []pm3.Api { + return []pm3.Api{ + pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/system/apikey", []string{"context", "query:apikey"}, []string{"apikey"}, p.systemAPIKeyController.Get), + pm3.CreateApiWidthDoc(http.MethodPost, "/api/v1/system/apikey", []string{"context", "body"}, nil, p.systemAPIKeyController.Create), + pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/system/apikey", []string{"context", "query:apikey", "body"}, nil, p.systemAPIKeyController.Update), + pm3.CreateApiWidthDoc(http.MethodDelete, "/api/v1/system/apikey", []string{"context", "query:apikey"}, nil, p.systemAPIKeyController.Delete), + pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/system/apikeys", []string{"context", "query:keyword"}, []string{"apikeys"}, p.systemAPIKeyController.Search), + pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/my/apikeys/", []string{"context"}, []string{"apikeys"}, p.systemAPIKeyController.MyAPIKeys), + pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/simple/system/apikeys", []string{"context"}, []string{"apikeys"}, p.systemAPIKeyController.SimpleList), + } +} diff --git a/plugins/openapi/check.go b/plugins/openapi/check.go index 4f5f50f7..b05031e5 100644 --- a/plugins/openapi/check.go +++ b/plugins/openapi/check.go @@ -2,6 +2,13 @@ package openapi import ( "strings" + "time" + + "github.com/eolinker/go-common/ignore" + + "github.com/eolinker/go-common/autowire" + + system_apikey "github.com/APIParkLab/APIPark/module/system-apikey" "github.com/eolinker/eosc/env" @@ -14,7 +21,8 @@ var ( ) type openapiCheck struct { - apikey string + apikey string + apikeyModule system_apikey.IAPIKeyModule `autowired:""` } func newOpenapiCheck() *openapiCheck { @@ -22,7 +30,9 @@ func newOpenapiCheck() *openapiCheck { if !has { apikey = defaultAPIKey } - return &openapiCheck{apikey: apikey} + p := &openapiCheck{apikey: apikey} + autowire.Autowired(p) + return p } func (o *openapiCheck) Check(method string, path string) (bool, []gin.HandlerFunc) { @@ -37,9 +47,38 @@ func (o *openapiCheck) Sort() int { } func (o *openapiCheck) Handler(ginCtx *gin.Context) { + notIgnore := !ignore.IsIgnorePath("openapi", ginCtx.Request.Method, ginCtx.FullPath()) + if !notIgnore { + return + } authorization := ginCtx.GetHeader("Authorization") if authorization == "" { + apikey, has := ginCtx.GetQuery("apikey") + if !has { + ginCtx.AbortWithStatusJSON(403, gin.H{"code": -8, "msg": "invalid token", "success": "fail"}) + return + } + authorization = apikey + } + if authorization == o.apikey { + return + } + list, err := o.apikeyModule.SimpleList(ginCtx) + if err != nil { ginCtx.AbortWithStatusJSON(403, gin.H{"code": -8, "msg": "invalid token", "success": "fail"}) return } + if len(list) == 0 { + ginCtx.AbortWithStatusJSON(403, gin.H{"code": -8, "msg": "invalid token", "success": "fail"}) + return + } + for _, item := range list { + if item.Value == authorization { + if item.Expired != 0 && item.Expired < time.Now().Unix() { + continue + } + return + } + } + ginCtx.AbortWithStatusJSON(403, gin.H{"code": -8, "msg": "invalid token", "success": "fail"}) } diff --git a/plugins/openapi/mcp.go b/plugins/openapi/mcp.go new file mode 100644 index 00000000..29b52606 --- /dev/null +++ b/plugins/openapi/mcp.go @@ -0,0 +1,25 @@ +package openapi + +import ( + "fmt" + "net/http" + "strings" + + "github.com/eolinker/go-common/ignore" + + mcp_server "github.com/APIParkLab/APIPark/mcp-server" + "github.com/eolinker/go-common/pm3" +) + +func (p *plugin) mcpAPIs() []pm3.Api { + globalMessagePath := fmt.Sprintf("/openapi/v1/%s/message", strings.Trim(mcp_server.GlobalBasePath, "/")) + serviceMessagePath := fmt.Sprintf("/openapi/v1/%s/:serviceId/message", strings.Trim(mcp_server.ServiceBasePath, "/")) + ignore.IgnorePath("openapi", http.MethodPost, globalMessagePath) + ignore.IgnorePath("openapi", http.MethodPost, serviceMessagePath) + return []pm3.Api{ + pm3.CreateApiSimple(http.MethodGet, fmt.Sprintf("/openapi/v1/%s/sse", strings.Trim(mcp_server.GlobalBasePath, "/")), p.mcpController.GlobalHandleSSE), + pm3.CreateApiSimple(http.MethodPost, globalMessagePath, p.mcpController.GlobalHandleMessage), + pm3.CreateApiSimple(http.MethodGet, fmt.Sprintf("/openapi/v1/%s/:serviceId/sse", strings.Trim(mcp_server.ServiceBasePath, "/")), p.mcpController.ServiceHandleSSE), + pm3.CreateApiSimple(http.MethodPost, serviceMessagePath, p.mcpController.ServiceHandleMessage), + } +} diff --git a/plugins/openapi/plugin.go b/plugins/openapi/plugin.go index 3082fb84..45303d52 100644 --- a/plugins/openapi/plugin.go +++ b/plugins/openapi/plugin.go @@ -2,6 +2,7 @@ package openapi import ( application_authorization "github.com/APIParkLab/APIPark/controller/application-authorization" + "github.com/APIParkLab/APIPark/controller/mcp" "github.com/eolinker/go-common/pm3" ) @@ -13,6 +14,7 @@ var ( type plugin struct { apis []pm3.Api authorizationController application_authorization.IAuthorizationController `autowired:""` + mcpController mcp.IMcpController `autowired:""` } func (p *plugin) Middlewares() []pm3.IMiddleware { @@ -30,4 +32,5 @@ func (p *plugin) Name() string { } func (p *plugin) OnComplete() { p.apis = p.appAuthorizationApis() + p.apis = append(p.apis, p.mcpAPIs()...) } diff --git a/resources/access/access.yaml b/resources/access/access.yaml index 64bbd001..bafec116 100644 --- a/resources/access/access.yaml +++ b/resources/access/access.yaml @@ -182,6 +182,26 @@ system: value: 'manager' dependents: - system.settings.strategy.view + - name: mcp + value: 'mcp' + children: + - name: view + value: 'view' + guest_allow: true + - name: manager + value: 'manager' + dependents: + - system.settings.mcp.view + - name: apikey + value: 'apikey' + children: + - name: view + value: 'view' + guest_allow: true + - name: manager + value: 'manager' + dependents: + - system.settings.apikey.view team: - name: service value: 'service' diff --git a/resources/access/role.yaml b/resources/access/role.yaml index 27c30a2e..6637cbae 100644 --- a/resources/access/role.yaml +++ b/resources/access/role.yaml @@ -18,12 +18,16 @@ system: - system.settings.ai_provider.view - system.settings.api_gateway.manager - system.settings.api_gateway.view + - system.settings.apikey.view + - system.settings.apikey.manager - system.settings.data_source.manager - system.settings.data_source.view - system.settings.general.manager - system.settings.general.view - system.settings.log_configuration.manager - system.settings.log_configuration.view + - system.settings.mcp.view + - system.settings.mcp.manager - system.settings.role.view - system.settings.ssl_certificate.manager - system.settings.ssl_certificate.view diff --git a/service/ai-api/iml.go b/service/ai-api/iml.go index 358e99f5..8a8126bd 100644 --- a/service/ai-api/iml.go +++ b/service/ai-api/iml.go @@ -23,6 +23,11 @@ type imlAPIService struct { universally.IServiceDelete } +func (i *imlAPIService) UpdateAIProvider(ctx context.Context, providerId string, ids ...string) error { + _, err := i.store.UpdateField(ctx, "provider", providerId, "uuid in (?)", ids) + return err +} + func (i *imlAPIService) CountByProvider(ctx context.Context, provider string) (int64, error) { return i.store.Count(ctx, "", map[string]interface{}{"provider": provider}) } diff --git a/service/ai-api/model.go b/service/ai-api/model.go index 8364c02f..f2154bfa 100644 --- a/service/ai-api/model.go +++ b/service/ai-api/model.go @@ -76,6 +76,7 @@ func FromEntity(e *api.AiAPIInfo) *API { Updater: e.Updater, Disable: e.Disable, UseToken: e.UseToken, + Provider: e.Provider, Type: e.Type, AdditionalConfig: cfg, } diff --git a/service/ai-api/service.go b/service/ai-api/service.go index 62ef5389..55385a08 100644 --- a/service/ai-api/service.go +++ b/service/ai-api/service.go @@ -17,6 +17,7 @@ type IAPIService interface { CountMapByModel(ctx context.Context, keyword string, conditions map[string]interface{}) (map[string]int64, error) CountByModel(ctx context.Context, model string) (int64, error) CountByProvider(ctx context.Context, provider string) (int64, error) + UpdateAIProvider(ctx context.Context, providerId string, ids ...string) error DeleteByService(ctx context.Context, serviceId string) error } diff --git a/service/ai-local/iml.go b/service/ai-local/iml.go index 5d21cc2b..f0c79f8a 100644 --- a/service/ai-local/iml.go +++ b/service/ai-local/iml.go @@ -20,6 +20,11 @@ type imlLocalModelService struct { universally.IServiceDelete } +func (i *imlLocalModelService) UpdateProvider(ctx context.Context, provider string, ids ...string) error { + _, err := i.store.UpdateField(ctx, "provider", provider, "uuid in (?)", ids) + return err +} + func (i *imlLocalModelService) DefaultModel(ctx context.Context) (*LocalModel, error) { info, err := i.store.First(ctx, map[string]interface{}{"state": 1}) if err != nil { diff --git a/service/ai-local/service.go b/service/ai-local/service.go index 2c493b36..61e67d1b 100644 --- a/service/ai-local/service.go +++ b/service/ai-local/service.go @@ -14,6 +14,7 @@ type ILocalModelService interface { universally.IServiceEdit[EditLocalModel] universally.IServiceDelete DefaultModel(ctx context.Context) (*LocalModel, error) + UpdateProvider(ctx context.Context, provider string, ids ...string) error } type ILocalModelPackageService interface { diff --git a/service/ai-model/iml.go b/service/ai-model/iml.go index 79d7faf7..9a4dc176 100644 --- a/service/ai-model/iml.go +++ b/service/ai-model/iml.go @@ -72,10 +72,13 @@ func (i *imlProviderModelService) CheckNameDuplicate(ctx context.Context, provid v, _ := i.store.First(ctx, map[string]interface{}{"provider": provider, "name": name}) if v == nil { return false - } else if excludeId != "" && v.UUID != excludeId { - return true + } else { + if excludeId == "" { + return true + } else { + return v.UUID != excludeId + } } - return false } func (i *imlProviderModelService) OnComplete() { diff --git a/service/ai/iml.go b/service/ai/iml.go index ad99617d..7e146f5c 100644 --- a/service/ai/iml.go +++ b/service/ai/iml.go @@ -70,8 +70,8 @@ type imlProviderService struct { // return i.store.Save(ctx, info) //} -func (i *imlProviderService) CheckNameDuplicate(ctx context.Context, name string) bool { - v, _ := i.store.First(ctx, map[string]interface{}{"name": name}) +func (i *imlProviderService) CheckUuidDuplicate(ctx context.Context, uuid string) bool { + v, _ := i.store.First(ctx, map[string]interface{}{"uuid": uuid}) return v != nil } diff --git a/service/ai/service.go b/service/ai/service.go index c7eb0071..26ac92c8 100644 --- a/service/ai/service.go +++ b/service/ai/service.go @@ -15,7 +15,7 @@ type IProviderService interface { universally.IServiceDelete //Save(ctx context.Context, id string, cfg *SetProvider) error //MaxPriority(ctx context.Context) (int, error) - CheckNameDuplicate(ctx context.Context, name string) bool + CheckUuidDuplicate(ctx context.Context, uuid string) bool } func init() { diff --git a/service/cluster/cluster.go b/service/cluster/cluster.go index 0550c748..490f5d49 100644 --- a/service/cluster/cluster.go +++ b/service/cluster/cluster.go @@ -80,7 +80,7 @@ func (s *imlClusterService) GetLabels(ctx context.Context, ids ...string) map[st } return map[string]string{o.UUID: o.Name} } - list, err := s.store.ListQuery(ctx, "uuid in ?", []interface{}{ids}, "id") + list, err := s.store.ListQuery(ctx, "uuid in (?)", []interface{}{ids}, "id") if err != nil { return nil } diff --git a/service/service-model-mapping/iml.go b/service/service-model-mapping/iml.go index 69b7ed5e..7ca3ce20 100644 --- a/service/service-model-mapping/iml.go +++ b/service/service-model-mapping/iml.go @@ -16,6 +16,14 @@ type imlServiceModelMappingService struct { store service.IServiceModelMappingStore `autowired:""` } +func (i *imlServiceModelMappingService) Delete(ctx context.Context, sid string) error { + _, err := i.store.DeleteWhere(ctx, map[string]interface{}{"sid": sid}) + if err != nil { + return err + } + return nil +} + func (i *imlServiceModelMappingService) Get(ctx context.Context, sid string) (*ModelMapping, error) { entity, err := i.store.First(ctx, map[string]interface{}{"sid": sid}) if err != nil { diff --git a/service/service-model-mapping/service.go b/service/service-model-mapping/service.go index 533614ce..4d4886fa 100644 --- a/service/service-model-mapping/service.go +++ b/service/service-model-mapping/service.go @@ -10,6 +10,7 @@ import ( type IServiceModelMappingService interface { Get(ctx context.Context, sid string) (*ModelMapping, error) Save(ctx context.Context, input *Save) error + Delete(ctx context.Context, sid string) error } func init() { diff --git a/service/service/iml.go b/service/service/iml.go index 8e42fea6..a2e42c1c 100644 --- a/service/service/iml.go +++ b/service/service/iml.go @@ -25,6 +25,22 @@ type imlServiceService struct { universally.IServiceEdit[Edit] } +func (i *imlServiceService) AppListByTeam(ctx context.Context, teamId ...string) ([]*Service, error) { + if len(teamId) == 0 { + return nil, fmt.Errorf("team id is empty") + } + w := map[string]interface{}{ + "team": teamId, + "as_app": true, + "is_delete": false, + } + list, err := i.store.List(ctx, w) + if err != nil { + return nil, err + } + return utils.SliceToSlice(list, FromEntity), nil +} + func (i *imlServiceService) ForceDelete(ctx context.Context, id string) error { _, err := i.store.DeleteWhere(ctx, map[string]interface{}{"uuid": id}) return err @@ -178,6 +194,7 @@ func createEntityHandler(i *Create) *service.Service { Catalogue: i.Catalogue, AsServer: i.AsServer, AsApp: i.AsApp, + EnableMCP: i.EnableMCP, } } func updateHandler(e *service.Service, i *Edit) { @@ -209,5 +226,8 @@ func updateHandler(e *service.Service, i *Edit) { if i.State != nil { e.State = *i.State } + if i.EnableMCP != nil { + e.EnableMCP = *i.EnableMCP + } e.UpdateAt = time.Now() } diff --git a/service/service/model.go b/service/service/model.go index bd8a1b3b..973c3b4e 100644 --- a/service/service/model.go +++ b/service/service/model.go @@ -122,6 +122,7 @@ type Service struct { State int AsServer bool AsApp bool + EnableMCP bool ApprovalType ApprovalType CreateTime time.Time UpdateTime time.Time @@ -145,6 +146,7 @@ func FromEntity(e *service.Service) *Service { ApprovalType: ToApprovalType(e.ApprovalType), AsServer: e.AsServer, AsApp: e.AsApp, + EnableMCP: e.EnableMCP, State: e.State, CreateTime: e.CreateAt, UpdateTime: e.UpdateAt, @@ -167,6 +169,7 @@ type Create struct { State int AsServer bool AsApp bool + EnableMCP bool } type Edit struct { @@ -179,6 +182,7 @@ type Edit struct { AdditionalConfig *map[string]string State *int ApprovalType *ApprovalType + EnableMCP *bool } type CreateTag struct { diff --git a/service/service/service.go b/service/service/service.go index a9f67ada..f4454960 100644 --- a/service/service/service.go +++ b/service/service/service.go @@ -20,6 +20,7 @@ type IServiceService interface { ServiceList(ctx context.Context, serviceIds ...string) ([]*Service, error) ServiceListByKind(ctx context.Context, kind Kind, serviceIds ...string) ([]*Service, error) AppList(ctx context.Context, appIds ...string) ([]*Service, error) + AppListByTeam(ctx context.Context, teamId ...string) ([]*Service, error) ForceDelete(ctx context.Context, id string) error } diff --git a/service/system-apikey/iml.go b/service/system-apikey/iml.go new file mode 100644 index 00000000..5fdbe57a --- /dev/null +++ b/service/system-apikey/iml.go @@ -0,0 +1,64 @@ +package system_apikey + +import ( + "time" + + "github.com/APIParkLab/APIPark/service/universally" + "github.com/APIParkLab/APIPark/stores/system" +) + +type imlAPIKeyService struct { + store system.IAPIKeyStore `autowired:""` + universally.IServiceGet[APIKey] + universally.IServiceDelete + universally.IServiceCreate[Create] + universally.IServiceEdit[Update] +} + +func (i *imlAPIKeyService) OnComplete() { + i.IServiceGet = universally.NewGet[APIKey, system.APIKey](i.store, FromEntity) + i.IServiceCreate = universally.NewCreator[Create, system.APIKey](i.store, "system_apikey", i.createEntityHandler, i.uniquestHandler, i.labelHandler) + i.IServiceDelete = universally.NewDelete[system.APIKey](i.store) + i.IServiceEdit = universally.NewEdit[Update, system.APIKey](i.store, i.updateHandler, i.labelHandler) +} + +func (i *imlAPIKeyService) idHandler(e *system.APIKey) int64 { + return e.Id +} +func (i *imlAPIKeyService) labelHandler(e *system.APIKey) []string { + return []string{e.Name} +} +func (i *imlAPIKeyService) uniquestHandler(t *Create) []map[string]interface{} { + return []map[string]interface{}{{"uuid": t.Id}} +} +func (i *imlAPIKeyService) createEntityHandler(t *Create) *system.APIKey { + now := time.Now() + return &system.APIKey{ + UUID: t.Id, + Name: t.Name, + Value: t.Value, + Expired: t.Expired, + CreateAt: now, + UpdateAt: now, + } +} + +func (i *imlAPIKeyService) updateHandler(e *system.APIKey, t *Update) { + isUpdate := false + if t.Name != nil { + e.Name = *t.Name + isUpdate = true + } + if t.Value != nil { + e.Value = *t.Value + isUpdate = true + } + if t.Expired != nil { + e.Expired = *t.Expired + isUpdate = true + } + if isUpdate { + e.UpdateAt = time.Now() + } + +} diff --git a/service/system-apikey/model.go b/service/system-apikey/model.go new file mode 100644 index 00000000..15e67e51 --- /dev/null +++ b/service/system-apikey/model.go @@ -0,0 +1,44 @@ +package system_apikey + +import ( + "time" + + "github.com/APIParkLab/APIPark/stores/system" +) + +type APIKey struct { + Id string + Name string + Value string + Creator string + Updater string + CreateAt time.Time + UpdateAt time.Time + Expired int64 +} + +type Create struct { + Id string + Name string + Value string + Expired int64 +} + +type Update struct { + Name *string + Value *string + Expired *int64 +} + +func FromEntity(s *system.APIKey) *APIKey { + return &APIKey{ + Id: s.UUID, + Name: s.Name, + Value: s.Value, + Creator: s.Creator, + Updater: s.Updater, + CreateAt: s.CreateAt, + UpdateAt: s.UpdateAt, + Expired: s.Expired, + } +} diff --git a/service/system-apikey/service.go b/service/system-apikey/service.go new file mode 100644 index 00000000..b195ec34 --- /dev/null +++ b/service/system-apikey/service.go @@ -0,0 +1,21 @@ +package system_apikey + +import ( + "reflect" + + "github.com/APIParkLab/APIPark/service/universally" + "github.com/eolinker/go-common/autowire" +) + +type IAPIKeyService interface { + universally.IServiceGet[APIKey] + universally.IServiceDelete + universally.IServiceCreate[Create] + universally.IServiceEdit[Update] +} + +func init() { + autowire.Auto[IAPIKeyService](func() reflect.Value { + return reflect.ValueOf(new(imlAPIKeyService)) + }) +} diff --git a/service/universally/get-softdelete.go b/service/universally/get-softdelete.go index e191fcf6..2f83ab2b 100644 --- a/service/universally/get-softdelete.go +++ b/service/universally/get-softdelete.go @@ -46,7 +46,7 @@ func (s *imlServiceGetSoftDelete[T, E]) List(ctx context.Context, uuids ...strin where = append(where, "uuid = ?") args = append(args, uuids[0]) } else { - where = append(where, "uuid in ?") + where = append(where, "uuid in (?)") args = append(args, uuids) } } diff --git a/service/universally/get.go b/service/universally/get.go index a9802b2f..db417f1c 100644 --- a/service/universally/get.go +++ b/service/universally/get.go @@ -59,7 +59,7 @@ func (s *imlServiceGet[T, E]) List(ctx context.Context, uuids ...string) ([]*T, where = append(where, "uuid = ?") args = append(args, uuids[0]) } else { - where = append(where, "uuid in ?") + where = append(where, "uuid in (?)") args = append(args, uuids) } } diff --git a/stores/ai/model.go b/stores/ai/model.go index 0eb2788b..56fda088 100644 --- a/stores/ai/model.go +++ b/stores/ai/model.go @@ -138,7 +138,8 @@ type LocalModelPackage struct { Name string `gorm:"type:varchar(100);not null;column:name;comment:名称"` Size string `gorm:"type:varchar(100);not null;column:size;comment:模型大小"` Hash string `gorm:"type:varchar(100);not null;column:hash;comment:模型hash"` - Description string `gorm:"type:varchar(255);not null;column:description;comment:描述"` + Description string `gorm:"type:varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;not null;column:description;comment:描述"` + Text string `gorm:"type:varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;not null;column:text;comment:描述"` Version string `gorm:"type:varchar(100);not null;column:version;comment:版本"` IsPopular bool `gorm:"type:tinyint(1);not null;column:is_popular;comment:是否热门"` } @@ -187,4 +188,3 @@ func (i *ProviderModel) TableName() string { func (i *ProviderModel) IdValue() int64 { return i.Id } - diff --git a/stores/service/model.go b/stores/service/model.go index 3e205ace..8ed2f0fd 100644 --- a/stores/service/model.go +++ b/stores/service/model.go @@ -24,6 +24,7 @@ type Service struct { ApprovalType int `gorm:"type:tinyint(4);not null;column:approval_type;comment:审核类型"` AsServer bool `gorm:"type:tinyint(1);not null;column:as_server;comment:是否为服务端项目"` AsApp bool `gorm:"type:tinyint(1);not null;column:as_app;comment:是否为应用项目"` + EnableMCP bool `gorm:"type:tinyint(1);not null;column:enable_mcp;comment:是否启用MCP"` } func (p *Service) IdValue() int64 { diff --git a/stores/setting/model.go b/stores/setting/model.go index 14e23342..95af11df 100644 --- a/stores/setting/model.go +++ b/stores/setting/model.go @@ -5,7 +5,7 @@ import "time" type Setting struct { Id int64 `gorm:"column:id;type:BIGINT(20);AUTO_INCREMENT;NOT NULL;comment:id;primary_key;comment:主键ID;"` Name string `gorm:"size:255;not null;column:name;comment:name;uniqueIndex:name;"` - Value string `gorm:"type:text;not null;column:value;comment:value;"` + Value string `gorm:"type:text;null;column:value;comment:value;"` CreateAt time.Time `gorm:"type:timestamp;NOT NULL;DEFAULT:CURRENT_TIMESTAMP;column:create_at;comment:创建时间"` UpdateAt time.Time `gorm:"type:timestamp;NOT NULL;DEFAULT:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;column:update_at;comment:修改时间"` Operator string `gorm:"type:varchar(36);not null;column:operator;comment:操作人;"` diff --git a/stores/system/model.go b/stores/system/model.go new file mode 100644 index 00000000..3929202f --- /dev/null +++ b/stores/system/model.go @@ -0,0 +1,22 @@ +package system + +import "time" + +type APIKey struct { + Id int64 `gorm:"column:id;type:BIGINT(20);AUTO_INCREMENT;NOT NULL;comment:id;primary_key;comment:主键ID;"` + UUID string `gorm:"type:varchar(36);not null;column:uuid;uniqueIndex:uuid;comment:UUID;"` + Name string `gorm:"type:varchar(100);not null;column:name;comment:name"` + Value string `gorm:"type:text;not null;column:value;comment:value;"` + Expired int64 `gorm:"type:int(11);not null;column:expired;comment:过期时间"` // 过期时间 + Creator string `gorm:"size:36;not null;column:creator;comment:创建人id" aovalue:"creator"` // 创建人id + Updater string `gorm:"size:36;not null;column:updater;comment:修改人id" aovalue:"updater"` // 修改人id + CreateAt time.Time `gorm:"type:timestamp;NOT NULL;DEFAULT:CURRENT_TIMESTAMP;column:create_at;comment:创建时间"` + UpdateAt time.Time `gorm:"type:timestamp;NOT NULL;DEFAULT:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;column:update_at;comment:修改时间"` +} + +func (a *APIKey) IdValue() int64 { + return a.Id +} +func (a *APIKey) TableName() string { + return "system_apikey" +} diff --git a/stores/system/store.go b/stores/system/store.go new file mode 100644 index 00000000..1526a93d --- /dev/null +++ b/stores/system/store.go @@ -0,0 +1,22 @@ +package system + +import ( + "reflect" + + "github.com/eolinker/go-common/autowire" + "github.com/eolinker/go-common/store" +) + +type IAPIKeyStore interface { + store.ISearchStore[APIKey] +} + +type imlAPIKeyStore struct { + store.SearchStore[APIKey] +} + +func init() { + autowire.Auto[IAPIKeyStore](func() reflect.Value { + return reflect.ValueOf(new(imlAPIKeyStore)) + }) +}