diff --git a/java/README.md b/java/README.md index fa6de481..a33e936b 100644 --- a/java/README.md +++ b/java/README.md @@ -12,7 +12,7 @@ com.baidubce qianfan - 0.0.9 + 0.1.0 ``` @@ -21,13 +21,13 @@ 对于Kotlin DSL,在build.gradle.kts的dependencies中添加依赖。 ```kotlin -implementation("com.baidubce:qianfan:0.0.9") +implementation("com.baidubce:qianfan:0.1.0") ``` 对于Groovy DSL,在build.gradle的dependencies中添加依赖。 ```groovy -implementation 'com.baidubce:qianfan:0.0.9' +implementation 'com.baidubce:qianfan:0.1.0' ``` > 我们提供了一些 [示例](./examples),可以帮助快速了解 SDK 的使用方法并完成常见功能。 diff --git a/java/example/pom.xml b/java/example/pom.xml index 52509fba..1de49559 100644 --- a/java/example/pom.xml +++ b/java/example/pom.xml @@ -18,7 +18,7 @@ com.baidubce qianfan - 0.0.9 + 0.1.0 diff --git a/java/example/src/main/java/com/baidubce/ConsoleExample.java b/java/example/src/main/java/com/baidubce/ConsoleExample.java new file mode 100644 index 00000000..9b214b7b --- /dev/null +++ b/java/example/src/main/java/com/baidubce/ConsoleExample.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2024 Baidu, Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.baidubce; + +import com.baidubce.qianfan.Qianfan; +import com.baidubce.qianfan.util.CollUtils; + +import java.util.Map; + +/** + * 本示例实现了Console管控API调用流程 + * API文档可见 API列表 + */ +public class ConsoleExample { + public static void main(String[] args) { + describePresetServices(); + describeTPMResource(); + } + + private static void describePresetServices() { + // 获取预置服务列表 https://cloud.baidu.com/doc/WENXINWORKSHOP/s/Glygmrg7v + Map response = new Qianfan().console() + // 对应文档中请求地址的后缀 + .route("/v2/service") + // 对应文档中Query参数的Action + .action("DescribePresetServices") + // 如果不传入任何Response类,则默认返回Map + .execute() + // 可以传入class或者TypeRef来指定反序列化后返回的Response类 + // .execute(DescribePresetServicesResponse.class) + .getResult(); + System.out.println(response); + } + + private static void describeTPMResource() { + // 查询TPM配额信息详情 https://cloud.baidu.com/doc/WENXINWORKSHOP/s/ultmls9l9 + Map response = new Qianfan().console().route("/v2/charge").action("DescribeTPMResource") + // 需要传入参数的场景,可以自行封装请求类,或者使用Map.of()来构建请求Body + // Java 8可以使用SDK提供的CollUtils.mapOf()来替代Map.of() + .body(CollUtils.mapOf( + "model", "ernie-4.0-8k", + "paymentTiming", "Postpaid" + )) + .execute() + .getResult(); + System.out.println(response); + } +} + + diff --git a/java/example/src/main/java/com/baidubce/SystemMemoryExample.java b/java/example/src/main/java/com/baidubce/SystemMemoryExample.java new file mode 100644 index 00000000..33263aef --- /dev/null +++ b/java/example/src/main/java/com/baidubce/SystemMemoryExample.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2024 Baidu, Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.baidubce; + +import com.baidubce.qianfan.Qianfan; +import com.baidubce.qianfan.core.auth.Auth; +import com.baidubce.qianfan.core.builder.MessageBuilder; +import com.baidubce.qianfan.model.chat.Message; +import com.baidubce.qianfan.util.CollUtils; + +import java.util.List; +import java.util.Map; + +/** + * 本示例实现了简易的系统记忆管理接口及推理接口的全流程调用 + * 系统记忆Console接口文档可见 创建系统记忆 + */ +public class SystemMemoryExample { + // 在模型服务-应用接入中创建应用,即可获得应用的AppID、API Key和Secret Key + private static final String APP_ID = "替换为实际的AppId"; + private static final String APP_API_KEY = "替换为实际的ApiKey"; + private static final String APP_SECRET_KEY = "替换为实际的SecretKey"; + + public static void main(String[] args) throws InterruptedException { + // 注意,在生产环境中,应当手动创建一个系统记忆并维护记忆内容,然后在推理中重复使用该系统记忆 + String systemMemoryId = createSystemMemory(APP_ID, "度小茶饮品店智能客服系统记忆"); + System.out.println("系统记忆ID:" + systemMemoryId); + + Boolean result = modifySystemMemory(systemMemoryId, CollUtils.listOf( + new MessageBuilder() + .add("user", "你的幸运数字是什么?") + .add("system", "我的幸运数字是42。") + .build(), + new MessageBuilder() + .add("user", "能推荐一款适合夏天饮用的饮品吗?") + .add("system", "当然可以,我们推荐冰镇柠檬绿茶,清新爽口,非常适合夏日消暑。") + .build() + )); + System.out.println("修改系统记忆结果:" + result); + + Thread.sleep(5000); + + Map memories = describeSystemMemory(systemMemoryId); + System.out.println("记忆列表:" + memories); + + String system = "你是度小茶饮品店的智能客服。"; + String response = chat(systemMemoryId, system, "你的幸运数字是什么"); + System.out.println("推理结果:" + response); + String response2 = chat(systemMemoryId, system, "推荐一个适合夏天的饮料"); + System.out.println("推理结果2:" + response2); + } + + private static String createSystemMemory(String appId, String description) { + return new Qianfan().console() + .route("/v2/memory") + .action("CreateSystemMemory") + .body(CollUtils.mapOf( + "appId", appId, + "description", description + )) + .execute(String.class) + .getResult(); + } + + private static Boolean modifySystemMemory(String systemMemoryId, List> memories) { + return new Qianfan().console() + .route("/v2/memory") + .action("ModifySystemMemory") + .body(CollUtils.mapOf( + "systemMemoryId", systemMemoryId, + "memories", memories + )) + .execute(Boolean.class) + .getResult(); + } + + private static Map describeSystemMemory(String systemMemoryId) { + return new Qianfan().console() + .route("/v2/memory") + .action("DescribeSystemMemory") + .body(CollUtils.mapOf( + "systemMemoryId", systemMemoryId + )) + .execute() + .getResult(); + } + + private static String chat(String systemMemoryId, String system, String query) { + // 使用系统记忆时,鉴权需要使用OAuth方式,同时需要传入与系统记忆相同应用的Api Key和Secret Key + return new Qianfan(Auth.TYPE_OAUTH, APP_API_KEY, APP_SECRET_KEY).chatCompletion() + .model("ERNIE-3.5-8K") + .system(system) + .enableSystemMemory(true) + .systemMemoryId(systemMemoryId) + .addUserMessage(query) + .execute() + .getResult(); + } +} diff --git a/java/pom.xml b/java/pom.xml index d1b6e285..265987d5 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -4,7 +4,7 @@ com.baidubce qianfan - 0.0.9 + 0.1.0 jar qianfan diff --git a/java/src/main/java/com/baidubce/qianfan/Qianfan.java b/java/src/main/java/com/baidubce/qianfan/Qianfan.java index 3f718124..04a33848 100644 --- a/java/src/main/java/com/baidubce/qianfan/Qianfan.java +++ b/java/src/main/java/com/baidubce/qianfan/Qianfan.java @@ -26,6 +26,8 @@ import com.baidubce.qianfan.model.chat.ChatResponse; import com.baidubce.qianfan.model.completion.CompletionRequest; import com.baidubce.qianfan.model.completion.CompletionResponse; +import com.baidubce.qianfan.model.console.ConsoleRequest; +import com.baidubce.qianfan.model.console.ConsoleResponse; import com.baidubce.qianfan.model.embedding.EmbeddingRequest; import com.baidubce.qianfan.model.embedding.EmbeddingResponse; import com.baidubce.qianfan.model.image.Image2TextRequest; @@ -37,6 +39,9 @@ import com.baidubce.qianfan.model.rerank.RerankRequest; import com.baidubce.qianfan.model.rerank.RerankResponse; +import java.lang.reflect.Type; + + public class Qianfan { private final QianfanClient client; @@ -138,6 +143,14 @@ public StreamIterator pluginStream(PluginRequest request) { return requestStream(request, PluginResponse.class); } + public ConsoleBuilder console() { + return new ConsoleBuilder(this); + } + + public ConsoleResponse console(ConsoleRequest request, Type type) { + return consoleRequest(request, type); + } + public , U extends BaseRequest> T request(BaseRequest request, Class responseClass) { return client.request(request, responseClass); } @@ -145,4 +158,8 @@ public , U extends BaseRequest> T request(BaseReque public , U extends BaseRequest> StreamIterator requestStream(BaseRequest request, Class responseClass) { return client.requestStream(request, responseClass); } + + public ConsoleResponse consoleRequest(ConsoleRequest request, Type type) { + return client.consoleRequest(request, type); + } } \ No newline at end of file diff --git a/java/src/main/java/com/baidubce/qianfan/QianfanClient.java b/java/src/main/java/com/baidubce/qianfan/QianfanClient.java index c3d938a9..29c48581 100644 --- a/java/src/main/java/com/baidubce/qianfan/QianfanClient.java +++ b/java/src/main/java/com/baidubce/qianfan/QianfanClient.java @@ -21,18 +21,27 @@ import com.baidubce.qianfan.core.RateLimiter; import com.baidubce.qianfan.core.StreamIterator; import com.baidubce.qianfan.core.auth.Auth; +import com.baidubce.qianfan.core.auth.IAMAuth; import com.baidubce.qianfan.core.auth.IAuth; import com.baidubce.qianfan.model.*; +import com.baidubce.qianfan.model.console.ConsoleRequest; +import com.baidubce.qianfan.model.console.ConsoleResponse; import com.baidubce.qianfan.model.exception.ApiException; +import com.baidubce.qianfan.model.exception.AuthException; import com.baidubce.qianfan.model.exception.QianfanException; import com.baidubce.qianfan.model.exception.RequestException; import com.baidubce.qianfan.util.Json; +import com.baidubce.qianfan.util.ParameterizedTypeImpl; import com.baidubce.qianfan.util.StringUtils; import com.baidubce.qianfan.util.function.ThrowingFunction; import com.baidubce.qianfan.util.http.*; +import java.lang.reflect.Type; + class QianfanClient { - private static final String SDK_VERSION = "0.0.9"; + private static final String SDK_VERSION = "0.1.0"; + private static final String CONSOLE_URL_NO_ACTION_TEMPLATE = "%s%s"; + private static final String CONSOLE_URL_ACTION_TEMPLATE = "%s%s?Action=%s"; private static final String QIANFAN_URL_TEMPLATE = "%s/rpc/2.0/ai_custom/v1/wenxinworkshop%s"; private static final String EXTRA_PARAM_REQUEST_SOURCE = "request_source"; private static final String REQUEST_SOURCE_PREFIX = "qianfan_java_sdk_v"; @@ -120,6 +129,36 @@ private , U, V, E extends Exception> V request( throw new IllegalStateException("Request failed with unknown error"); } + public ConsoleResponse consoleRequest(ConsoleRequest request, Type type) { + try { + if (!(auth instanceof IAMAuth)) { + throw new AuthException("Console request requires IAM authentication"); + } + String url = StringUtils.isNotEmpty(request.getAction()) + ? String.format(CONSOLE_URL_ACTION_TEMPLATE, QianfanConfig.getConsoleApiBaseUrl(), request.getRoute(), request.getAction()) + : String.format(CONSOLE_URL_NO_ACTION_TEMPLATE, QianfanConfig.getConsoleApiBaseUrl(), request.getRoute()); + HttpRequest httpRequest = HttpClient.request() + .post(url) + .body(request.getBody() == null ? new Object() : request.getBody()); + + Type respType = new ParameterizedTypeImpl(ConsoleResponse.class, new Type[]{type}); + HttpResponse> resp = auth.signRequest(httpRequest).executeJson(respType); + + if (resp.getCode() != HttpStatus.SUCCESS) { + throw new RequestException(String.format("Request failed with status code %d: %s", resp.getCode(), resp.getStringBody())); + } + ApiErrorResponse errorResp = Json.deserialize(resp.getStringBody(), ApiErrorResponse.class); + if (StringUtils.isNotEmpty(errorResp.getErrorMsg())) { + throw new ApiException("Request failed with api error", errorResp); + } + return resp.getBody(); + } catch (QianfanException e) { + throw e; + } catch (Exception e) { + throw new RequestException(String.format("Request failed: %s", e.getMessage()), e); + } + } + private , U, V, E extends Exception> V innerRequest( BaseRequest request, ThrowingFunction, E> reqProcessor, diff --git a/java/src/main/java/com/baidubce/qianfan/core/StreamIterator.java b/java/src/main/java/com/baidubce/qianfan/core/StreamIterator.java index 031302de..66647422 100644 --- a/java/src/main/java/com/baidubce/qianfan/core/StreamIterator.java +++ b/java/src/main/java/com/baidubce/qianfan/core/StreamIterator.java @@ -23,7 +23,6 @@ import com.baidubce.qianfan.util.http.SSEIterator; import java.io.Closeable; -import java.io.IOException; import java.util.Iterator; import java.util.Map; import java.util.Objects; diff --git a/java/src/main/java/com/baidubce/qianfan/core/builder/ConsoleBuilder.java b/java/src/main/java/com/baidubce/qianfan/core/builder/ConsoleBuilder.java new file mode 100644 index 00000000..c419d184 --- /dev/null +++ b/java/src/main/java/com/baidubce/qianfan/core/builder/ConsoleBuilder.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2024 Baidu, Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.baidubce.qianfan.core.builder; + +import com.baidubce.qianfan.Qianfan; +import com.baidubce.qianfan.model.console.ConsoleRequest; +import com.baidubce.qianfan.model.console.ConsoleResponse; +import com.baidubce.qianfan.model.exception.ValidationException; +import com.baidubce.qianfan.util.TypeRef; + +import java.lang.reflect.Type; +import java.util.Map; + +public class ConsoleBuilder { + private Qianfan qianfan; + + private String route; + + private String action; + + private Object body; + + public ConsoleBuilder() { + super(); + } + + public ConsoleBuilder(Qianfan qianfan) { + this.qianfan = qianfan; + } + + public ConsoleBuilder route(String route) { + this.route = route; + return this; + } + + public ConsoleBuilder action(String action) { + this.action = action; + return this; + } + + public ConsoleBuilder body(Object body) { + this.body = body; + return this; + } + + public ConsoleRequest build() { + return new ConsoleRequest() + .setRoute(route) + .setAction(action) + .setBody(body); + } + + public ConsoleResponse> execute() { + return executeWithCheck(new TypeRef>() {}.getType()); + } + + public ConsoleResponse execute(TypeRef typeRef) { + return executeWithCheck(typeRef.getType()); + } + + public ConsoleResponse execute(Class clazz) { + return executeWithCheck(clazz); + } + + public ConsoleResponse execute(Type type) { + return executeWithCheck(type); + } + + private ConsoleResponse executeWithCheck(Type type) { + if (qianfan == null) { + throw new ValidationException("Qianfan client is not set. " + + "please create builder from Qianfan client, " + + "or use build() instead of execute() to get Request and send it by yourself."); + } + return qianfan.consoleRequest(build(), type); + } +} diff --git a/java/src/main/java/com/baidubce/qianfan/model/console/ConsoleRequest.java b/java/src/main/java/com/baidubce/qianfan/model/console/ConsoleRequest.java new file mode 100644 index 00000000..d6e222b1 --- /dev/null +++ b/java/src/main/java/com/baidubce/qianfan/model/console/ConsoleRequest.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2024 Baidu, Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.baidubce.qianfan.model.console; + +public class ConsoleRequest { + /** + * 请求路由,例如/v2/service + * route、action可参考文档: API列表 + */ + private String route; + + /** + * 请求操作,例如DescribePresetServices + */ + private String action; + + /** + * 请求发送的POST数据 + */ + private Object body; + + public String getRoute() { + return route; + } + + public ConsoleRequest setRoute(String route) { + this.route = route; + return this; + } + + public String getAction() { + return action; + } + + public ConsoleRequest setAction(String action) { + this.action = action; + return this; + } + + public Object getBody() { + return body; + } + + public ConsoleRequest setBody(Object body) { + this.body = body; + return this; + } +} diff --git a/java/src/main/java/com/baidubce/qianfan/model/console/ConsoleResponse.java b/java/src/main/java/com/baidubce/qianfan/model/console/ConsoleResponse.java index 38085813..ec60aaeb 100644 --- a/java/src/main/java/com/baidubce/qianfan/model/console/ConsoleResponse.java +++ b/java/src/main/java/com/baidubce/qianfan/model/console/ConsoleResponse.java @@ -22,20 +22,20 @@ public class ConsoleResponse { /** * 请求ID */ - @JsonProp("logId") - private String logId; + @JsonProp("requestId") + private String requestId; /** * 请求结果 */ private T result; - public String getLogId() { - return logId; + public String getRequestId() { + return requestId; } - public ConsoleResponse setLogId(String logId) { - this.logId = logId; + public ConsoleResponse setRequestId(String requestId) { + this.requestId = requestId; return this; } diff --git a/java/src/main/java/com/baidubce/qianfan/util/CollUtils.java b/java/src/main/java/com/baidubce/qianfan/util/CollUtils.java new file mode 100644 index 00000000..85a9c3f8 --- /dev/null +++ b/java/src/main/java/com/baidubce/qianfan/util/CollUtils.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2024 Baidu, Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.baidubce.qianfan.util; + +import java.util.*; + +public class CollUtils { + + private CollUtils() { + } + + public static Map mapOf(Object... keyValues) { + if (keyValues.length % 2 != 0) { + throw new IllegalArgumentException("Invalid key-value pairs"); + } + + Map map = new LinkedHashMap<>(); + for (int i = 0; i < keyValues.length; i += 2) { + @SuppressWarnings("unchecked") + K key = (K) keyValues[i]; + @SuppressWarnings("unchecked") + V value = (V) keyValues[i + 1]; + if (key == null) { + throw new IllegalArgumentException("Key at index " + i + " is null"); + } + map.put(key, value); + } + return map; + } + + @SafeVarargs + public static List listOf(T... values) { + List list = new ArrayList<>(); + Collections.addAll(list, values); + return list; + } + + @SafeVarargs + public static T[] arrayOf(T... values) { + return values; + } + + @SafeVarargs + public static Set setOf(T... values) { + Set set = new LinkedHashSet<>(); + Collections.addAll(set, values); + return set; + } +} diff --git a/java/src/main/java/com/baidubce/qianfan/util/Json.java b/java/src/main/java/com/baidubce/qianfan/util/Json.java index 54791495..3332433d 100644 --- a/java/src/main/java/com/baidubce/qianfan/util/Json.java +++ b/java/src/main/java/com/baidubce/qianfan/util/Json.java @@ -40,6 +40,14 @@ public static String serialize(Object object) { return GSON.toJson(object); } + public static T deserialize(String json, TypeRef typeRef) { + return GSON.fromJson(json, typeRef.getType()); + } + + public static T deserialize(String json, Class clazz) { + return GSON.fromJson(json, clazz); + } + public static T deserialize(String json, Type type) { return GSON.fromJson(json, type); } diff --git a/java/src/main/java/com/baidubce/qianfan/util/ParameterizedTypeImpl.java b/java/src/main/java/com/baidubce/qianfan/util/ParameterizedTypeImpl.java new file mode 100644 index 00000000..04fc9464 --- /dev/null +++ b/java/src/main/java/com/baidubce/qianfan/util/ParameterizedTypeImpl.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2024 Baidu, Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.baidubce.qianfan.util; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +public class ParameterizedTypeImpl implements ParameterizedType { + private final Class raw; + private final Type[] args; + + public ParameterizedTypeImpl(Class raw, Type[] args) { + this.raw = raw; + this.args = args; + } + + @Override + public Type[] getActualTypeArguments() { + return args; + } + + @Override + public Type getRawType() { + return raw; + } + + @Override + public Type getOwnerType() { + return null; + } +}