Skip to content

Commit

Permalink
feat: 浏览器增加鉴权开关 & 流式数据切分优化 (#689)
Browse files Browse the repository at this point in the history
* feat: 浏览器增加鉴权开关 & 流式数据切分优化

* fix: 鉴权开关字段修改& 更新版本号

* fix: 鉴权开关字段修改& 更新版本号

---------

Co-authored-by: wangting31 <[email protected]>
  • Loading branch information
wangting829 and wangting31 authored Jul 23, 2024
1 parent 265aaff commit 72ba467
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 65 deletions.
2 changes: 1 addition & 1 deletion javascript/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@baiducloud/qianfan",
"version": "0.1.5",
"version": "0.1.6-beta.0",
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org/"
Expand Down
7 changes: 5 additions & 2 deletions javascript/src/Base/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export class BaseClient {
protected headers = DEFAULT_HEADERS;
protected fetchInstance;
protected fetchConfig: FetchConfig;
protected enableOauth: boolean;
access_token = '';
expires_in = 0;

Expand All @@ -49,6 +50,7 @@ export class BaseClient {
QIANFAN_LLM_API_RETRY_BACKOFF_FACTOR?: string;
QIANFAN_LLM_API_RETRY_COUNT?: string;
QIANFAN_LLM_RETRY_MAX_WAIT_INTERVAL?: string;
ENABLE_OAUTH: boolean;
Endpoint?: string;
}) {
const defaultConfig = getDefaultConfig();
Expand All @@ -66,6 +68,7 @@ export class BaseClient {
= options?.QIANFAN_LLM_API_RETRY_BACKOFF_FACTOR ?? defaultConfig.QIANFAN_LLM_API_RETRY_BACKOFF_FACTOR;
this.qianfanLlmApiRetryCount
= options?.QIANFAN_LLM_API_RETRY_COUNT ?? defaultConfig.QIANFAN_LLM_API_RETRY_COUNT;
this.enableOauth = options?.ENABLE_OAUTH ?? defaultConfig.ENABLE_OAUTH;
this.controller = new AbortController();
this.fetchInstance = new Fetch({
maxRetries: Number(this.qianfanLlmApiRetryCount),
Expand Down Expand Up @@ -141,8 +144,8 @@ export class BaseClient {
stream = false
): Promise<Resp | AsyncIterableType> {
let fetchOptions;
// 如果baseUrl是aip.baidubce.com,证明用户未配置proxy url,则认为需要放开鉴权
if (this.qianfanBaseUrl.includes('aip.baidubce.com')) {
// 如果enableOauth开启, 则放开鉴权
if (this.enableOauth) {
// 检查鉴权信息
if (!(this.qianfanAccessKey && this.qianfanSecretKey) && !(this.qianfanAk && this.qianfanSk)) {
throw new Error('请设置AK/SK或QIANFAN_ACCESS_KEY/QIANFAN_SECRET_KEY');
Expand Down
1 change: 1 addition & 0 deletions javascript/src/constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export const DEFAULT_CONFIG: DefaultConfig = {
QIANFAN_RPM_LIMIT: '',
QIANFAN_TPM_LIMIT: '',
version: '1',
ENABLE_OAUTH: false,
};

export const RETRY_CODE = [
Expand Down
14 changes: 12 additions & 2 deletions javascript/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ import Embedding from './Embedding';
import Plugin from './Plugin';
import {Text2Image, Image2Text} from './Images';
import Reranker from './Reranker';
import {setEnvVariable} from './utils';
import {setEnvVariable, setBrowserVariable} from './utils';

export {ChatCompletion, Completions, Embedding, Plugin, Text2Image, Image2Text, Reranker, setEnvVariable};
export {
ChatCompletion,
Completions,
Embedding,
Plugin,
Text2Image,
Image2Text,
Reranker,
setEnvVariable,
setBrowserVariable,
};
2 changes: 2 additions & 0 deletions javascript/src/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ export interface DefaultConfig {
QIANFAN_RPM_LIMIT: string;
QIANFAN_TPM_LIMIT: string;
version: string;
// 浏览器字段是否开启鉴权,是则使用鉴权,否则不使用鉴权
ENABLE_OAUTH: boolean;
}

/**
Expand Down
99 changes: 41 additions & 58 deletions javascript/src/streaming/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,15 @@ class SSEDecoder {
}

decode(line: string) {
if (line.endsWith('\r')) {
line = line.substring(0, line.length - 1);
}

if (!line) {
if (!this.event && !this.data.length) {
return null;
}

const sse: ServerSentEvent = {
event: this.event,
data: this.data.join('\n'),
data: this.data.join(''),
raw: this.chunks,
};

this.event = null;
this.data = [];
this.chunks = [];
Expand Down Expand Up @@ -88,33 +82,12 @@ class LineDecoder {
textDecoder: TextDecoder;

constructor() {
this.buffer = [];
this.textDecoder = new TextDecoder('utf-8');
}

decode(chunk: Bytes): string[] {
decode(chunk: Bytes): string {
let text = this.decodeText(chunk);
if (!text) {
return [];
}

const trailingNewline = text.endsWith('\n') || text.endsWith('\r');
let lines = text.split(LineDecoder.NEWLINE_REGEXP);

if (lines.length === 1 && !trailingNewline) {
this.buffer.push(lines[0]);
return [];
}
if (this.buffer.length > 0) {
lines = [this.buffer.join('') + lines[0], ...lines.slice(1)];
this.buffer = [];
}

if (!trailingNewline) {
this.buffer = [lines.pop() || ''];
}

return lines;
return text;
}

decodeText(bytes: Bytes): string {
Expand Down Expand Up @@ -164,35 +137,20 @@ export class Stream<Item> implements AsyncIterable<Item> {
}

const lineDecoder = new LineDecoder();
let buffer = new Uint8Array(); // 初始化缓存的 Buffer
let previousChunkLastByte: number | null = null;
let buffer = new Uint8Array();
const iter = readableStreamAsyncIterable<Bytes>(response.body);

for await (const chunk of iter) {
if (previousChunkLastByte === 10) {
buffer = concatUint8Arrays(buffer, chunk as Uint8Array);

for (const line of lineDecoder.decode(buffer)) {
const sse = decoder.decode(line);
if (sse) {
yield sse;
}
buffer = concatUint8Arrays(buffer, chunk as Uint8Array);
// 按换行符(ASCII 码 10)分割 buffer
const [lines, remaining] = splitUint8Array(buffer, 10);
for (const line of lines) {
const lineStr = lineDecoder.decode(line);
const sse = decoder.decode(lineStr);
if (sse) {
yield sse;
}

buffer = new Uint8Array();
}
else {
buffer = concatUint8Arrays(buffer, chunk as Uint8Array);
}
// 保存当前 chunk 的最后一个字节
previousChunkLastByte = chunk[chunk.length - 1] as number; ;
}

for (const line of lineDecoder.flush()) {
const sse = decoder.decode(line);
if (sse) {
yield sse;
}
buffer = remaining;
}
}

Expand All @@ -207,7 +165,6 @@ export class Stream<Item> implements AsyncIterable<Item> {
if (done) {
continue;
}

if (sse.data.startsWith('[DONE]')) {
done = true;
continue;
Expand All @@ -227,8 +184,9 @@ export class Stream<Item> implements AsyncIterable<Item> {
if (data && data.error) {
throw new Error(data.error);
}

yield data;
if (data) {
yield data;
}
}
}
done = true;
Expand Down Expand Up @@ -413,3 +371,28 @@ export function readableStreamAsyncIterable<T>(stream: any): AsyncIterableIterat
},
};
}


/**
* 使用指定的分隔符将 Uint8Array 数组拆分为多个子数组和剩余部分。
*
* @param array 要拆分的 Uint8Array 数组。
* @param delimiter 分隔符的数值。
* @returns 包含拆分后的多个子数组和剩余部分的数组。
* 第一个元素是拆分后的子数组列表,类型为 Uint8Array[]。
* 第二个元素是剩余部分的 Uint8Array 数组。
*/
function splitUint8Array(array: Uint8Array, delimiter: number): [Uint8Array[], Uint8Array] {
const result: Uint8Array[] = [];
let start = 0;

for (let i = 0; i < array.length; i++) {
if (array[i] === delimiter) {
result.push(array.subarray(start, i));
start = i + 1; // 跳过 delimiter
}
}

// 返回分割后的数组和剩余的部分
return [result, array.subarray(start)];
}
20 changes: 18 additions & 2 deletions javascript/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
// limitations under the License.

import {BASE_PATH, DEFAULT_CONFIG} from './constant';
import {IAMConfig, QfLLMInfoMap, ReqBody} from './interface';
import {IAMConfig, QfLLMInfoMap, ReqBody, DefaultConfig} from './interface';
import * as packageJson from '../package.json';

/**
Expand Down Expand Up @@ -143,7 +143,7 @@ export function readEnvVariable(key: string) {
*
* @returns 返回一个字符串类型的键值对对象,包含环境变量
*/
export function getDefaultConfig(): Record<string, string> {
export function getDefaultConfig(): DefaultConfig {
const envVariables = Object.keys(DEFAULT_CONFIG);
if (getCurrentEnvironment() === 'browser') {
return {...DEFAULT_CONFIG};
Expand Down Expand Up @@ -286,3 +286,19 @@ export function parseHeaders(headers): {[key: string]: string} {
});
return headerObj;
}

interface Variables {
[key: string]: any;
}

/**
* 设置浏览器变量
*
* @param variables 要设置的变量对象,其中每个属性名对应一个变量名,属性值对应变量的值
* @returns 无返回值
*/
export function setBrowserVariable(variables: Variables): void {
Object.entries(variables).forEach(([key, value]) => {
DEFAULT_CONFIG[key] = value;
});
}

0 comments on commit 72ba467

Please sign in to comment.