Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions example/s.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
edition: 3.0.0
name: ai-model-app
access: quanxi

resources:
modelDemo:
component: fc3@dev
props:
region: cn-shanghai
runtime: custom-container
functionName: ${env('fc_component_function_name', 'ai-model-test-qwen')}
description: model service from functionai test
logConfig: auto
vpcConfig: auto
nasConfig: auto
instanceConcurrency: 20
cpu: 8
memorySize: 65536
diskSize: 10240
timeout: 300
gpuConfig:
gpuMemorySize: 49152
gpuType: fc.gpu.ada.1
customContainerConfig:
image: >-
serverless-registry.cn-hangzhou.cr.aliyuncs.com/functionai/dms-vllm:openai_v0.10.0
port: 9000
entrypoint:
- vllm
- serve
- /mnt/${env('fc_component_function_name', 'ai-model-test-qwen')}/${env('MODEL_ID', 'Qwen/Qwen3-32B-AWQ')}
- --port
- "9000"
- --served-model-name
- ${env('MODEL_ID', 'Qwen/Qwen3-32B-AWQ')}
# - Qwen/Qwen3-14B
- --tensor-parallel-size
- "1"
- --trust-remote-code # 添加这个参数
- --max-model-len
- "4096"
triggers: # 默认,用户可能关注的是开启 authType 是 bear token
- triggerConfig:
methods:
- GET
- POST
- PUT
- DELETE
authType: anonymous
disableURLInternet: false
triggerName: httpTrigger
description: ''
qualifier: LATEST
triggerType: http
provisionConfig:
target: 1
alwaysAllocateCPU: false
alwaysAllocateGPU: false
mode: sync

supplement:
modelConfig:
source: modelscope
id: ${env('MODEL_ID', 'Qwen/Qwen3-32B-AWQ')}
# id: Qwen/Qwen3-14B
storage: nas
role: acs:ram::${config('AccountID')}:role/aliyundevsdefaultrole

125 changes: 125 additions & 0 deletions example/test_models.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#!/bin/bash

# 定义日志文件
LOG_FILE="test_models.log"

# 清空或创建日志文件
> "$LOG_FILE"

# 日志记录函数
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}

# 定义model_id列表
MODEL_IDS=("Qwen/Qwen2-Audio-7B-Instruct" "Qwen/Qwen2.5-0.5B-Instruct" "Qwen/Qwen3-32B-AWQ" "Qwen/Qwen3-14B" "Qwen/QwQ-32B-AWQ")

# 检查yaml文件是否存在
YAML_FILE="s.yaml"
if [ ! -f "$YAML_FILE" ]; then
log "Error: $YAML_FILE not found!"
exit 1
fi

# 遍历每个model_id
for MODEL_ID in "${MODEL_IDS[@]}"; do
log "========================================"
log "Testing model: $MODEL_ID"
log "========================================"

# 生成随机函数名
RANDOM_STRING=$(openssl rand -hex 16)
export fc_component_function_name=ai-model-qwen-$RANDOM_STRING
export NEW_MODEL_SERVICE_CLIENT_CONNECT_TIMEOUT=10000
export MODEL_ID=$MODEL_ID

# 下载模型
log "Downloading model..."
DOWNLOAD_OUTPUT=$(s model download 2>&1)
echo "$DOWNLOAD_OUTPUT" >> "$LOG_FILE"
echo "$DOWNLOAD_OUTPUT"
if echo "$DOWNLOAD_OUTPUT" | grep -q "Error"; then
log "Failed to download model: $MODEL_ID"
continue
fi

# 部署服务
log "Deploying..."
DEPLOY_OUTPUT=$(s deploy 2>&1)
echo "$DEPLOY_OUTPUT" >> "$LOG_FILE"
echo "$DEPLOY_OUTPUT"

# 检查部署是否成功
if echo "$DEPLOY_OUTPUT" | grep -q "state:.*Active"; then
log "Deployment successful for model: $MODEL_ID"
else
log "Deployment failed for model: $MODEL_ID"
# 清理资源
REMOVE_OUTPUT=$(s model remove -y 2>&1)
echo "$REMOVE_OUTPUT" >> "$LOG_FILE"
echo "$REMOVE_OUTPUT"
REMOVE_OUTPUT=$(s remove -y 2>&1)
echo "$REMOVE_OUTPUT" >> "$LOG_FILE"
echo "$REMOVE_OUTPUT"
continue
fi

# 提取system_url
SYSTEM_URL=$(echo "$DEPLOY_OUTPUT" | grep "system_url:" | sed 's/.*system_url: *//' | tr -d ' "[:cntrl:]')
if [ -z $SYSTEM_URL ]; then
log "Failed to extract system_url for model: $MODEL_ID"
# 清理资源
REMOVE_OUTPUT=$(s model remove -y 2>&1)
echo "$REMOVE_OUTPUT" >> "$LOG_FILE"
echo "$REMOVE_OUTPUT"
REMOVE_OUTPUT=$(s remove -y 2>&1)
echo "$REMOVE_OUTPUT" >> "$LOG_FILE"
echo "$REMOVE_OUTPUT"
continue
fi
log "Extracted system_url: $SYSTEM_URL"

# 发送测试请求
log "Sending test request..."
CURL_OUTPUT=$(curl --request POST \
--url "$SYSTEM_URL/v1/chat/completions" \
-H "Content-Type: application/json" \
--data '{
"model": "$MODEL_ID",
"messages": [
{
"role": "user",
"content": "Hello! 你是谁?"
}
],
"stream": false
}' 2>&1)

echo "$CURL_OUTPUT" >> "$LOG_FILE"
echo "$CURL_OUTPUT"

# 检查curl请求是否成功
if echo "$CURL_OUTPUT" | grep -q '"object":"chat.completion"'; then
log "Model test successful for: $MODEL_ID"
# 提取并显示模型回复内容
RESPONSE_CONTENT=$(echo "$CURL_OUTPUT" | sed -n 's/.*"content":"\([^"]*\)".*/\1/p' | sed 's/\\n/\n/g' | sed 's/\\t/\t/g')
log "Model response: $RESPONSE_CONTENT"
else
log "Model test failed for: $MODEL_ID"
fi

# 清理资源
log "Removing resources..."
REMOVE_OUTPUT=$(s model remove -y 2>&1)
echo "$REMOVE_OUTPUT" >> "$LOG_FILE"
echo "$REMOVE_OUTPUT"
REMOVE_OUTPUT=$(s remove -y 2>&1)
echo "$REMOVE_OUTPUT" >> "$LOG_FILE"
echo "$REMOVE_OUTPUT"

log ""
log "Finished testing model: $MODEL_ID"
log ""
done

log "All models tested."
20 changes: 20 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"author": "",
"license": "ISC",
"dependencies": {
"@alicloud/devs20230714": "^2.4.5",
"@alicloud/fc2": "^2.6.6",
"@alicloud/fc20230330": "4.3.4",
"@alicloud/pop-core": "^1.8.0",
Expand All @@ -34,6 +35,7 @@
"@serverless-devs/utils": "^0.0.17",
"@serverless-devs/zip": "^0.0.3-beta.8",
"ajv": "^8.17.1",
"ali-oss": "6.18.1",
"aliyun-sdk": "^1.12.10",
"chalk": "^4.1.0",
"crc64-ecma182.js": "^2.0.2",
Expand All @@ -51,8 +53,7 @@
"temp-dir": "^2.0.0",
"tty-table": "^4.2.3",
"uuid": "^9.0.1",
"uuid-by-string": "^4.0.0",
"ali-oss": "6.18.1"
"uuid-by-string": "^4.0.0"
},
"devDependencies": {
"@serverless-devs/component-interface": "^0.0.6",
Expand Down
22 changes: 22 additions & 0 deletions src/commands-help/model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export default {
help: {
description: 'Resource layer operation ',
summary: 'Resource layer operation ',
},
subCommands: {
download: {
help: {
description: `Download model`,
summary: 'Publish new layer version',
option: [],
},
},
reomve: {
help: {
description: `Remove model`,
summary: 'Remove model',
option: [],
},
},
},
};
7 changes: 7 additions & 0 deletions src/default/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,10 @@ export const VPC_AND_NAS_NAME = process.env.FC_GENERATE_VPC_AND_NAS_NAME || defa
const defaultLogStoreName = 'default-logs';
export const PROJECT = process.env.FC_GENERATE_PROJECT_NAME;
export const LOG_STORE = process.env.FC_GENERATE_LOGSTORE_NAME || defaultLogStoreName;

export function getEnvVariable(key: string): string {
if (typeof process !== 'undefined' && process.env) {
return process.env[key] || '';
}
return '';
}
8 changes: 8 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import SYaml2To3 from './subCommands/2to3';
import Logs from './subCommands/logs';
import { SCHEMA_FILE_PATH } from './constant';
import { checkDockerIsOK, isAppCenter, isYunXiao } from './utils';
import { Model } from './subCommands/model';

export default class Fc extends Base {
// 部署函数
Expand Down Expand Up @@ -168,6 +169,13 @@ export default class Fc extends Base {
return await logs.run();
}

public async model(inputs: IInputs) {
await super.handlePreRun(inputs, false);
const model = new Model(inputs);
logger.debug(`model inputs: ${model.subCommand}`);
return await model[model.subCommand]();
}

public async getSchema(inputs: IInputs) {
logger.debug(`getSchema: ${JSON.stringify(inputs)}`);
return fs.readFileSync(SCHEMA_FILE_PATH, 'utf-8');
Expand Down
1 change: 1 addition & 0 deletions src/interface/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export interface IProps extends IFunction {
concurrencyConfig?: IConcurrencyConfig;
provisionConfig?: IProvisionConfig;
endpoint?: string;
supplement?: any;
}

export interface IInputs extends _IInputs {
Expand Down
17 changes: 11 additions & 6 deletions src/subCommands/deploy/impl/function.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import _ from 'lodash';
import _, { isEmpty } from 'lodash';
import { diffConvertYaml } from '@serverless-devs/diff';
import inquirer from 'inquirer';
import fs from 'fs';
Expand Down Expand Up @@ -319,7 +319,7 @@ export default class Service extends Base {
* 生成 auto 资源,非 FC 资源,主要指 vpc、nas、log、role(oss mount 挂载点才有)
*/
private async _deployAuto() {
const { region } = this.inputs.props;
const { region, supplement } = this.inputs.props;
const { credential } = this.inputs;
const { functionName } = this.local;

Expand Down Expand Up @@ -390,14 +390,19 @@ vpcConfig:
_.set(this.local, 'vpcConfig', vpcConfig);
}
if (nasAuto) {
let modelConfig;
if (!isEmpty(supplement)) {
modelConfig = supplement.modelConfig;
}

logger.write(
yellow(`Created nas resource succeeded, please replace nasConfig: auto in yaml with:
nasConfig:
groupId: 0
userId: 0
mountPoints:
- serverAddr: ${mountTargetDomain}:/${functionName}
mountDir: /mnt/${functionName}
- serverAddr: ${mountTargetDomain}:/${functionName}${isEmpty(modelConfig) ? '' : '/' + modelConfig.id}
mountDir: /mnt/${functionName}${isEmpty(modelConfig) ? '' : '/' + modelConfig.id}
enableTLS: false\n`),
);
this.createResource.nas = { mountTargetDomain, fileSystemId };
Expand All @@ -406,8 +411,8 @@ nasConfig:
userId: 0,
mountPoints: [
{
serverAddr: `${mountTargetDomain}:/${functionName}`,
mountDir: `/mnt/${functionName}`,
serverAddr: `${mountTargetDomain}:/${functionName}${isEmpty(modelConfig) ? '' : '/' + modelConfig.id}`,
mountDir: `/mnt/${functionName}${isEmpty(modelConfig) ? '' : '/' + modelConfig.id}`,
enableTLS: false,
},
],
Expand Down
Loading