Skip to content

Commit

Permalink
feat: add aigc
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaohuoni committed Dec 12, 2023
1 parent 45a1bf1 commit 0577075
Show file tree
Hide file tree
Showing 9 changed files with 2,515 additions and 2,426 deletions.
6 changes: 6 additions & 0 deletions .changeset/tidy-pans-remember.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@alita/plugin-azure': patch
'alita': patch
---

feat: add aigc
5 changes: 1 addition & 4 deletions examples/with-azure/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,7 @@ export default defineConfig({
hash: false,
reactRouter5Compat: {},
exportStatic: {},
plugins: [
require.resolve('@alita/plugin-azure'),
require.resolve('./pluginazure'),
],
plugins: [require.resolve('./pluginazure')],
azure: {
apiVersion: '2023-07-01-preview',
model: 'alita4',
Expand Down
2 changes: 1 addition & 1 deletion examples/with-azure/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
"name": "@examples/azure",
"private": true,
"scripts": {
"call": "alita call",
"dev": "alita dev",
"build": "alita build",
"start": "npm run dev"
},
"dependencies": {
"@alita/flow": "workspace:*",
"@alita/plugin-azure": "workspace:*",
"alita": "workspace:*",
"antd-mobile": "^5.10.0"
}
Expand Down
1 change: 1 addition & 0 deletions packages/alita/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"@alita/types": "3.1.2",
"@alita/native": "3.2.10",
"@alita/plugins": "3.3.7",
"@alita/plugin-azure": "3.0.5",
"@umijs/core": "4.0.81",
"@umijs/bundler-webpack": "4.0.81",
"@umijs/babel-preset-umi": "4.0.81",
Expand Down
2 changes: 2 additions & 0 deletions packages/alita/src/preset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ export default (api: IApi) => {
// require.resolve('./features/qrcodeterminal'),
require.resolve('./commands/env'),
require.resolve('./commands/generate/pages'),
// aigc
require.resolve('@alita/plugin-azure'),
];
const plugins = [
require.resolve('@alita/plugins/dist/aconsole'),
Expand Down
3 changes: 2 additions & 1 deletion packages/plugin-azure/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@alita/plugin-azure",
"version": "3.0.1",
"version": "3.0.5",
"description": "@alita/plugin-azure",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand All @@ -26,6 +26,7 @@
"access": "public"
},
"dependencies": {
"@umijs/utils": "4.0.81",
"openai": "^4.19.1"
}
}
161 changes: 120 additions & 41 deletions packages/plugin-azure/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import { IApi } from 'umi';
import { dirname } from 'path';
import { dirname, join } from 'path';
import { logger, winPath } from '@umijs/utils';
import OpenAI from 'openai';
import { getPinoLogByLevel } from './pinolog';

export enum GeneratorType {
generate = 'generate',
enable = 'enable',
}

export interface IEvent<T> {
(fn: { (args: T): void }): void;
(args: {
Expand All @@ -16,10 +24,13 @@ export declare type IAzureSend = (
content: string | Array<OpenAI.ChatCompletionMessageParam>,
) => Promise<OpenAI.Chat.Completions.ChatCompletion>;
export declare type IOnIntlAzure = IEvent<{
openapi: OpenAI;
openai: OpenAI;
send: IAzureSend;
}>;
let sendAi: IAzureSend;

const errorMessage =
'环境变量 AZURE_OPENAI_API_KEY 不存在,无法使用 AIGC 相关的功能,建议在 .env.local 文件中添加它';
export default (api: IApi) => {
api.describe({
key: 'azure',
Expand All @@ -38,79 +49,101 @@ export default (api: IApi) => {
},
});
api.registerMethod({ name: 'onIntlAzure' });

// only dev
if (!['dev', 'setup'].includes(api.name)) return;
const apiKey = process.env['AZURE_OPENAI_API_KEY'];

api.onStart(async () => {
if (!apiKey) {
await api.applyPlugins({
key: 'onIntlAzure',
type: api.ApplyPluginsType.event,
args: {
openai: {},
send: async () => {
logger.error(errorMessage);
return {};
},
},
});
return;
}
const {
resource,
model = 'gpt-4',
apiVersion = '2023-08-01-preview',
} = api.config.azure;
const apiKey = process.env['AZURE_OPENAI_API_KEY'];
const openai = new OpenAI({
apiKey,
baseURL: `https://${resource}.openai.azure.com/openai/deployments/${model}`,
defaultQuery: { 'api-version': apiVersion },
defaultHeaders: { 'api-key': apiKey },
});
sendAi = async (
content: string | Array<OpenAI.ChatCompletionMessageParam>,
): Promise<OpenAI.Chat.Completions.ChatCompletion> => {
let message: Array<OpenAI.ChatCompletionMessageParam> = [];
if (typeof content === 'string') {
message = [
{
role: 'user',
content,
},
];
}
logger.info(JSON.stringify(message));
const result = await openai.chat.completions.create({
model,
messages: [
{
role: 'system',
content: `||>instructions:\你是一位高级软件工程师,你精通各种语言和方案\n||>assistant:\n`,
},
...message,
],
});
return result;
};
await api.applyPlugins({
key: 'onIntlAzure',
type: api.ApplyPluginsType.event,
args: {
openai,
send: async (
content: string | Array<OpenAI.ChatCompletionMessageParam>,
): Promise<OpenAI.Chat.Completions.ChatCompletion> => {
let message: Array<OpenAI.ChatCompletionMessageParam> = [];
if (typeof content === 'string') {
message = [
{
role: 'user',
content,
},
];
}
const result = await openai.chat.completions.create({
model,
messages: [
{
role: 'system',
content: `||>instructions:\你是一位高级软件工程师,你精通各种语言和方案\n||>assistant:\n`,
},
...message,
],
});
return result;
},
send: sendAi,
},
});
});
api.modifyDefaultConfig((memo: any) => {
const apiKey = process.env['AZURE_OPENAI_API_KEY'];
if (!apiKey) {
throw new Error(
'The AZURE_OPENAI_API_KEY environment variable is missing or empty.',
);
if (apiKey) {
memo.define = {
'process.env.AZURE_OPENAI_API_KEY': apiKey,
...memo.define,
};
}

memo.define = {
'process.env.AZURE_OPENAI_API_KEY': apiKey,
...memo.define,
};
return memo;
});
// 生成临时文件
api.onGenerateFiles({
fn() {
if (!apiKey) {
api.writeTmpFile({
path: 'index.tsx',
content: `
export const openai = {} as any;
export const sendOpenAI = async (content: string | Array<OpenAI.ChatCompletionMessageParam>):Promise<any> => {
console.error('${errorMessage}');
// result.choices[0]!.message?.content
// 兜底
return {choices:[{message:{content:'${errorMessage}'}}]};
}`,
});
return;
}
const openaiPath = dirname(require.resolve('openai'));
const {
resource,
model = 'gpt-4',
apiVersion = '2023-08-01-preview',
} = api.config.azure;

api.writeTmpFile({
path: 'index.tsx',
content: `
Expand Down Expand Up @@ -149,4 +182,50 @@ export default (api: IApi) => {
});
},
});

api.registerCommand({
name: 'call',
description: 'AIGC 问答',
fn: async ({ args }) => {
if (!apiKey) {
logger.error(errorMessage);
return;
}
const name = args?._?.[0] ?? args?._?.[0];
const result = await sendAi(name);
const content = result.choices[0]!.message?.content || '{}';
if (content) {
logger.info(content);
}
},
});

api.registerCommand({
name: 'why',
description: '当服务异常退出时,可以使用 why 命令,尝试查询一下原因',
fn: async ({}) => {
if (!apiKey) {
logger.error(errorMessage);
return;
}
const errorLog: any[] = await getPinoLogByLevel(
50,
winPath(
join(api.paths.absNodeModulesPath, '.cache', 'logger', 'umi.log'),
),
);
console.log(errorLog);
if (errorLog.length > 0) {
const result = await sendAi(
`我在使用 umi 系列框架的时候,遇到这个问题,服务挂了,请帮我分析一下可能的原因,并提供可能的修复方式,日志如下${JSON.stringify(
errorLog.pop(),
)}`,
);
const content = result.choices[0]!.message?.content || '{}';
if (content) {
console.log(content);
}
}
},
});
};
36 changes: 36 additions & 0 deletions packages/plugin-azure/src/pinolog.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import fs from 'fs';
import readline from 'readline';
import stream from 'stream';

export const getPinoLogByLevel = async (
lever: number,
path: string,
): Promise<any[]> => {
return new Promise((resolve, reject) => {
// 创建读取和写入的streams
const instream = fs.createReadStream(path);
const outstream = new stream();
// @ts-ignore
const rl = readline.createInterface(instream, outstream);

let errorLogs: any[] = [];

rl.on('line', function (line) {
// 将每一行转换成 JSON 对象
let logObject = JSON.parse(line);

// 检查日志等级是否为 'error'
if (logObject.level === lever) {
errorLogs.push(logObject);
}
});

rl.on('close', function () {
resolve(errorLogs);
});
rl.on('error', (e) => {
console.error('读取日志文件错误', path);
reject(e);
});
});
};
Loading

0 comments on commit 0577075

Please sign in to comment.