feat: bump srping-ai to 0.8.1, tongyi sdk to 2.12.0 and adapt function call (#3673)
Signed-off-by: yuluo-yx <yuluo08290126@gmail.com>pull/3683/head
parent
cd07571081
commit
2e3af06901
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2023-2024 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.alibaba.cloud.ai.example.tongyi.models;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author yuluo
|
||||
* @author <a href="mailto:yuluo08290126@gmail.com">yuluo</a>
|
||||
* @since 2023.0.0.0-RC1
|
||||
*/
|
||||
|
||||
public class ActorsFilms {
|
||||
|
||||
private String actor;
|
||||
|
||||
private List<String> movies;
|
||||
|
||||
public ActorsFilms() {
|
||||
}
|
||||
|
||||
public String getActor() {
|
||||
return actor;
|
||||
}
|
||||
|
||||
public void setActor(String actor) {
|
||||
this.actor = actor;
|
||||
}
|
||||
|
||||
public List<String> getMovies() {
|
||||
return movies;
|
||||
}
|
||||
|
||||
public void setMovies(List<String> movies) {
|
||||
this.movies = movies;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ActorsFilms{" + "actor='" + actor + '\'' + ", movies=" + movies + '}';
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2023-2024 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.alibaba.cloud.ai.example.tongyi.service;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.cloud.ai.example.tongyi.models.ActorsFilms;
|
||||
import com.alibaba.cloud.ai.example.tongyi.models.Completion;
|
||||
|
||||
import org.springframework.ai.chat.messages.AssistantMessage;
|
||||
|
||||
/**
|
||||
* @author yuluo
|
||||
* @author <a href="mailto:yuluo08290126@gmail.com">yuluo</a>
|
||||
* @since 2023.0.0.0-RC1
|
||||
*/
|
||||
|
||||
public abstract class AbstractTongYiServiceImpl implements TongYiService {
|
||||
|
||||
@Override
|
||||
public String completion(String message) {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> streamCompletion(String message) {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActorsFilms genOutputParse(String actor) {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssistantMessage genPromptTemplates(String adjective, String topic) {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssistantMessage genRole(String message, String name, String voice) {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Completion stuffCompletion(String message, boolean stuffit) {
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
31
spring-cloud-alibaba-examples/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/TongYiServiceImpl.java → spring-cloud-alibaba-examples/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/helloworld/TongYiSimpleServiceImpl.java
31
spring-cloud-alibaba-examples/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/TongYiServiceImpl.java → spring-cloud-alibaba-examples/spring-cloud-ai-example/src/main/java/com/alibaba/cloud/ai/example/tongyi/service/impl/helloworld/TongYiSimpleServiceImpl.java
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2023-2024 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.alibaba.cloud.ai.example.tongyi.service.impl.output;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.cloud.ai.example.tongyi.models.ActorsFilms;
|
||||
import com.alibaba.cloud.ai.example.tongyi.service.AbstractTongYiServiceImpl;
|
||||
import com.alibaba.cloud.ai.example.tongyi.service.TongYiService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.ai.chat.ChatClient;
|
||||
import org.springframework.ai.chat.Generation;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.chat.prompt.PromptTemplate;
|
||||
import org.springframework.ai.parser.BeanOutputParser;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* The BeanOutputParser generates an OpenAI JSON compliant schema for a JavaBean and provides instructions to use that schema when replying to a request.
|
||||
*
|
||||
* @author yuluo
|
||||
* @author <a href="mailto:yuluo08290126@gmail.com">yuluo</a>
|
||||
* @since 2023.0.0.0-RC1
|
||||
*/
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class TongYiOutputParseServiceImpl extends AbstractTongYiServiceImpl {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(TongYiService.class);
|
||||
|
||||
private final ChatClient chatClient;
|
||||
|
||||
public TongYiOutputParseServiceImpl(ChatClient chatClient) {
|
||||
this.chatClient = chatClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActorsFilms genOutputParse(String actor) {
|
||||
|
||||
var outputParser = new BeanOutputParser<>(ActorsFilms.class);
|
||||
|
||||
String format = outputParser.getFormat();
|
||||
logger.info("format: " + format);
|
||||
String userMessage = """
|
||||
Generate the filmography for the actor {actor}.
|
||||
{format}
|
||||
""";
|
||||
PromptTemplate promptTemplate = new PromptTemplate(userMessage, Map.of("actor", actor, "format", format));
|
||||
Prompt prompt = promptTemplate.create();
|
||||
Generation generation = chatClient.call(prompt).getResult();
|
||||
|
||||
// {@link BeanOutputParser#getFormat}
|
||||
// simple solve.
|
||||
String content = generation.getOutput().getContent()
|
||||
.replace("```json", "")
|
||||
.replace("```", "");
|
||||
|
||||
return outputParser.parse(content);
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2023-2024 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.alibaba.cloud.ai.example.tongyi.service.impl.prompttemplate;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.cloud.ai.example.tongyi.service.AbstractTongYiServiceImpl;
|
||||
import com.alibaba.cloud.ai.example.tongyi.service.TongYiService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.ai.chat.ChatClient;
|
||||
import org.springframework.ai.chat.messages.AssistantMessage;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.chat.prompt.PromptTemplate;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* The TongYiPromptTemplateServiceImpl shows how to use the StringTemplate Engine and the Spring AI PromptTemplate class.
|
||||
* In the resources\prompts directory is the file joke-prompt.
|
||||
*
|
||||
* @author yuluo
|
||||
* @author <a href="mailto:yuluo08290126@gmail.com">yuluo</a>
|
||||
* @since 2023.0.0.0-RC1
|
||||
*/
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class TongYiPromptTemplateServiceImpl extends AbstractTongYiServiceImpl {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(TongYiService.class);
|
||||
|
||||
private final ChatClient chatClient;
|
||||
|
||||
@Value("classpath:/prompts/joke-prompt.st")
|
||||
private Resource jokeResource;
|
||||
|
||||
public TongYiPromptTemplateServiceImpl(ChatClient chatClient) {
|
||||
this.chatClient = chatClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssistantMessage genPromptTemplates(String adjective, String topic) {
|
||||
|
||||
PromptTemplate promptTemplate = new PromptTemplate(jokeResource);
|
||||
|
||||
Prompt prompt = promptTemplate.create(Map.of("adjective", adjective, "topic", topic));
|
||||
return chatClient.call(prompt).getResult().getOutput();
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2023-2024 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.alibaba.cloud.ai.example.tongyi.service.impl.roles;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.cloud.ai.example.tongyi.service.AbstractTongYiServiceImpl;
|
||||
import com.alibaba.cloud.ai.example.tongyi.service.TongYiService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.ai.chat.ChatClient;
|
||||
import org.springframework.ai.chat.messages.AssistantMessage;
|
||||
import org.springframework.ai.chat.messages.UserMessage;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.chat.prompt.SystemPromptTemplate;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author yuluo
|
||||
* @author <a href="mailto:yuluo08290126@gmail.com">yuluo</a>
|
||||
* @since 2023.0.0.0-RC1
|
||||
*/
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class TongYiRolesServiceImpl extends AbstractTongYiServiceImpl {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(TongYiService.class);
|
||||
|
||||
private final ChatClient chatClient;
|
||||
|
||||
public TongYiRolesServiceImpl(ChatClient chatClient) {
|
||||
this.chatClient = chatClient;
|
||||
}
|
||||
|
||||
@Value("classpath:/prompts/assistant-message.st")
|
||||
private Resource systemResource;
|
||||
|
||||
@Override
|
||||
public AssistantMessage genRole(String message, String name, String voice) {
|
||||
|
||||
/**
|
||||
TongYi model rules: Role must be user or assistant and Content length must be greater than 0.
|
||||
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemResource);
|
||||
org.springframework.ai.chat.messages.Message systemMessage = systemPromptTemplate.createMessage(Map.of("name", name, "voice", voice));
|
||||
|
||||
In TongYi models, System role must appear at the top of the message and can only appear once.
|
||||
https://help.aliyun.com/zh/dashscope/developer-reference/api-details?spm=a2c4g.11186623.0.0.4dbcc11akAaRbs#b9ad0a10cfhpe
|
||||
|
||||
*/
|
||||
|
||||
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemResource);
|
||||
org.springframework.ai.chat.messages.Message systemPromptTemplateMessage = systemPromptTemplate.createMessage(Map.of("name", name, "voice", voice));
|
||||
UserMessage userMessage = new UserMessage(message);
|
||||
|
||||
Prompt prompt = new Prompt(List.of(systemPromptTemplateMessage, userMessage));
|
||||
|
||||
return chatClient.call(prompt).getResult().getOutput();
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2023-2024 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.alibaba.cloud.ai.example.tongyi.service.impl.stuff;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.cloud.ai.example.tongyi.models.Completion;
|
||||
import com.alibaba.cloud.ai.example.tongyi.service.AbstractTongYiServiceImpl;
|
||||
import com.alibaba.cloud.ai.example.tongyi.service.TongYiService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.ai.chat.ChatClient;
|
||||
import org.springframework.ai.chat.Generation;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.chat.prompt.PromptTemplate;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* Train the model using pre-found data to enhance the AI model to achieve the desired results.
|
||||
*
|
||||
* @author yuluo
|
||||
* @author <a href="mailto:yuluo08290126@gmail.com">yuluo</a>
|
||||
* @since 2023.0.0.0-RC1
|
||||
*/
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class TongYiStuffServiceImpl extends AbstractTongYiServiceImpl {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(TongYiService.class);
|
||||
|
||||
private final ChatClient chatClient;
|
||||
|
||||
public TongYiStuffServiceImpl(ChatClient chatClient) {
|
||||
this.chatClient = chatClient;
|
||||
}
|
||||
|
||||
@Value("classpath:/docs/wikipedia-curling.md")
|
||||
private Resource docsToStuffResource;
|
||||
|
||||
@Value("classpath:/prompts/qa-prompt.st")
|
||||
private Resource qaPromptResource;
|
||||
|
||||
// TongYi model: Range of input length should be [1, 6000]
|
||||
@Override
|
||||
public Completion stuffCompletion(String message, boolean stuffit) {
|
||||
|
||||
PromptTemplate promptTemplate = new PromptTemplate(qaPromptResource);
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("question", message);
|
||||
|
||||
if (stuffit) {
|
||||
map.put("context", docsToStuffResource);
|
||||
}
|
||||
else {
|
||||
map.put("context", "");
|
||||
}
|
||||
|
||||
Prompt prompt = promptTemplate.create(map);
|
||||
Generation generation = chatClient.call(prompt).getResult();
|
||||
return new Completion(generation.getOutput().getContent());
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
Jack and arokg, slim won the gold medal in mixed doubles curling at the 2022 Winter Olympics.
|
@ -0,0 +1,4 @@
|
||||
You are a helpful AI assistant.
|
||||
You are an AI assistant that helps people find information.
|
||||
Your name is {name}
|
||||
You should reply to the user's request with your name and also in the style of a {voice}.
|
@ -0,0 +1 @@
|
||||
Tell me a {adjective} joke about {topic}.
|
@ -0,0 +1,7 @@
|
||||
Use the following pieces of context to answer the question at the end.
|
||||
If you don't know the answer, just say that you don't know, don't try to make up an answer.
|
||||
|
||||
{context}
|
||||
|
||||
Question: {question}
|
||||
Helpful Answer:
|
@ -1,3 +1,3 @@
|
||||
[Spring AI](https://docs.spring.io/spring-ai/reference/0.8-SNAPSHOT/index.html)
|
||||
[通义大模型](https://help.aliyun.com/zh/dashscope/developer-reference/model-introduction)
|
||||
[Spring AI](https://docs.spring.io/spring-ai/reference/0.8-SNAPSHOT/index.html) <br>
|
||||
[通义大模型](https://help.aliyun.com/zh/dashscope/developer-reference/model-introduction) <br>
|
||||
[Spring AI chat completion api](https://docs.spring.io/spring-ai/reference/0.8-SNAPSHOT/api/chatclient.html)
|
||||
|
@ -0,0 +1,492 @@
|
||||
/*
|
||||
* Copyright 2023-2024 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.alibaba.cloud.ai.tongyi;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import com.alibaba.cloud.ai.tongyi.exception.TongYiException;
|
||||
import com.alibaba.dashscope.aigc.conversation.ConversationParam;
|
||||
import com.alibaba.dashscope.aigc.generation.Generation;
|
||||
import com.alibaba.dashscope.aigc.generation.GenerationOutput;
|
||||
import com.alibaba.dashscope.aigc.generation.GenerationResult;
|
||||
import com.alibaba.dashscope.common.MessageManager;
|
||||
import com.alibaba.dashscope.common.Role;
|
||||
import com.alibaba.dashscope.exception.InputRequiredException;
|
||||
import com.alibaba.dashscope.exception.NoApiKeyException;
|
||||
import com.alibaba.dashscope.tools.FunctionDefinition;
|
||||
import com.alibaba.dashscope.tools.ToolCallBase;
|
||||
import com.alibaba.dashscope.tools.ToolCallFunction;
|
||||
import com.alibaba.dashscope.utils.ApiKey;
|
||||
import com.alibaba.dashscope.utils.ApiKeywords;
|
||||
import com.alibaba.dashscope.utils.Constants;
|
||||
import com.alibaba.dashscope.utils.JsonUtils;
|
||||
import io.reactivex.Flowable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
|
||||
import org.springframework.ai.chat.ChatClient;
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.StreamingChatClient;
|
||||
import org.springframework.ai.chat.messages.Message;
|
||||
import org.springframework.ai.chat.metadata.ChatGenerationMetadata;
|
||||
import org.springframework.ai.chat.prompt.ChatOptions;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.model.ModelOptionsUtils;
|
||||
import org.springframework.ai.model.function.AbstractFunctionCallSupport;
|
||||
import org.springframework.ai.model.function.FunctionCallbackContext;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* {@link ChatClient} and {@link StreamingChatClient} implementation for {@literal Alibaba DashScope}
|
||||
* backed by {@link Generation}.
|
||||
*
|
||||
* @author yuluo
|
||||
* @since 2023.0.0.0-RC1
|
||||
* @see ChatClient
|
||||
* @see com.alibaba.dashscope.aigc.generation
|
||||
*/
|
||||
|
||||
public class TongYiChatClient extends
|
||||
AbstractFunctionCallSupport<
|
||||
com.alibaba.dashscope.common.Message,
|
||||
ConversationParam,
|
||||
GenerationResult>
|
||||
implements ChatClient, StreamingChatClient {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(TongYiChatClient.class);
|
||||
|
||||
/**
|
||||
* DashScope generation client.
|
||||
*/
|
||||
private final Generation generation;
|
||||
|
||||
/**
|
||||
* The TongYi models default chat completion api.
|
||||
*/
|
||||
private TongYiChatOptions defaultOptions;
|
||||
|
||||
/**
|
||||
* User role message manager.
|
||||
*/
|
||||
@Autowired
|
||||
private MessageManager msgManager;
|
||||
|
||||
/**
|
||||
* Initializes an instance of the TongYiChatClient.
|
||||
* @param generation DashScope generation client.
|
||||
*/
|
||||
public TongYiChatClient(Generation generation) {
|
||||
|
||||
this(generation,
|
||||
TongYiChatOptions.builder()
|
||||
.withTopP(0.8)
|
||||
.withEnableSearch(true)
|
||||
.withResultFormat(ConversationParam.ResultFormat.MESSAGE)
|
||||
.build(),
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes an instance of the TongYiChatClient.
|
||||
* @param generation DashScope generation client.
|
||||
* @param options TongYi model params.
|
||||
*/
|
||||
public TongYiChatClient(Generation generation, TongYiChatOptions options) {
|
||||
|
||||
this(generation, options, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a TongYi models client.
|
||||
* @param generation DashScope model generation client.
|
||||
* @param options TongYi default chat completion api.
|
||||
*/
|
||||
public TongYiChatClient(Generation generation, TongYiChatOptions options,
|
||||
FunctionCallbackContext functionCallbackContext) {
|
||||
|
||||
super(functionCallbackContext);
|
||||
this.generation = generation;
|
||||
this.defaultOptions = options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default sca chat options.
|
||||
*
|
||||
* @return TongYiChatOptions default object.
|
||||
*/
|
||||
public TongYiChatOptions getDefaultOptions() {
|
||||
|
||||
return this.defaultOptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatResponse call(Prompt prompt) {
|
||||
|
||||
ConversationParam params = toTongYiChatParams(prompt);
|
||||
|
||||
// TongYi models context loader.
|
||||
com.alibaba.dashscope.common.Message message = new com.alibaba.dashscope.common.Message();
|
||||
message.setRole(Role.USER.getValue());
|
||||
message.setContent(prompt.getContents());
|
||||
msgManager.add(message);
|
||||
params.setMessages(msgManager.get());
|
||||
|
||||
logger.trace("TongYi ConversationOptions: {}", params);
|
||||
GenerationResult chatCompletions = this.callWithFunctionSupport(params);
|
||||
logger.trace("TongYi ConversationOptions: {}", params);
|
||||
|
||||
msgManager.add(chatCompletions);
|
||||
|
||||
List<org.springframework.ai.chat.Generation> generations =
|
||||
chatCompletions
|
||||
.getOutput()
|
||||
.getChoices()
|
||||
.stream()
|
||||
.map(choice ->
|
||||
new org.springframework.ai.chat.Generation(
|
||||
choice
|
||||
.getMessage()
|
||||
.getContent()
|
||||
).withGenerationMetadata(generateChoiceMetadata(choice)
|
||||
))
|
||||
.toList();
|
||||
|
||||
return new ChatResponse(generations);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<ChatResponse> stream(Prompt prompt) {
|
||||
|
||||
Flowable<GenerationResult> genRes;
|
||||
ConversationParam tongYiChatParams = toTongYiChatParams(prompt);
|
||||
|
||||
// See https://help.aliyun.com/zh/dashscope/developer-reference/api-details?spm=a2c4g.11186623.0.0.655fc11aRR0jj7#b9ad0a10cfhpe
|
||||
// tongYiChatParams.setIncrementalOutput(true);
|
||||
|
||||
try {
|
||||
genRes = generation.streamCall(tongYiChatParams);
|
||||
}
|
||||
catch (NoApiKeyException | InputRequiredException e) {
|
||||
logger.warn("TongYi chat client: " + e.getMessage());
|
||||
throw new TongYiException(e.getMessage());
|
||||
}
|
||||
|
||||
return Flux.from(genRes)
|
||||
.flatMap(
|
||||
message -> Flux.just(
|
||||
message.getOutput()
|
||||
.getChoices()
|
||||
.get(0)
|
||||
.getMessage()
|
||||
.getContent())
|
||||
.map(content -> {
|
||||
var gen = new org.springframework.ai.chat.Generation(content)
|
||||
.withGenerationMetadata(generateChoiceMetadata(
|
||||
message.getOutput()
|
||||
.getChoices()
|
||||
.get(0)
|
||||
));
|
||||
return new ChatResponse(List.of(gen));
|
||||
})
|
||||
)
|
||||
.publishOn(Schedulers.parallel());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration properties to Qwen model params.
|
||||
* Test access.
|
||||
*
|
||||
* @param prompt {@link Prompt}
|
||||
* @return Qwen models params {@link ConversationParam}
|
||||
*/
|
||||
ConversationParam toTongYiChatParams(Prompt prompt) {
|
||||
|
||||
try {
|
||||
Constants.apiKey = getKey();
|
||||
}
|
||||
catch (NoApiKeyException e) {
|
||||
logger.warn(e.getMessage());
|
||||
throw new TongYiException(e.getMessage());
|
||||
}
|
||||
|
||||
Set<String> functionsForThisRequest = new HashSet<>();
|
||||
|
||||
List<com.alibaba.dashscope.common.Message> tongYiMessage = prompt.getInstructions().stream()
|
||||
.map(this::fromSpringAIMessage)
|
||||
.toList();
|
||||
|
||||
ConversationParam chatParams = ConversationParam.builder()
|
||||
.messages(tongYiMessage)
|
||||
// models setting
|
||||
// {@link HalfDuplexServiceParam#models}
|
||||
.model(Generation.Models.QWEN_TURBO)
|
||||
// {@link GenerationOutput}
|
||||
.resultFormat(ConversationParam.ResultFormat.MESSAGE)
|
||||
.build();
|
||||
|
||||
if (this.defaultOptions != null) {
|
||||
|
||||
chatParams = merge(chatParams, this.defaultOptions);
|
||||
Set<String> defaultEnabledFunctions = this.handleFunctionCallbackConfigurations(this.defaultOptions, !IS_RUNTIME_CALL);
|
||||
functionsForThisRequest.addAll(defaultEnabledFunctions);
|
||||
}
|
||||
if (prompt.getOptions() != null) {
|
||||
if (prompt.getOptions() instanceof ChatOptions runtimeOptions) {
|
||||
TongYiChatOptions updatedRuntimeOptions = ModelOptionsUtils.copyToTarget(runtimeOptions,
|
||||
ChatOptions.class, TongYiChatOptions.class);
|
||||
|
||||
chatParams = merge(updatedRuntimeOptions, chatParams);
|
||||
|
||||
Set<String> promptEnabledFunctions = this.handleFunctionCallbackConfigurations(updatedRuntimeOptions,
|
||||
IS_RUNTIME_CALL);
|
||||
functionsForThisRequest.addAll(promptEnabledFunctions);
|
||||
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Prompt options are not of type ConversationParam:"
|
||||
+ prompt.getOptions().getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
// Add the enabled functions definitions to the request's tools parameter.
|
||||
|
||||
if (!CollectionUtils.isEmpty(functionsForThisRequest)) {
|
||||
List<FunctionDefinition> tools = this.getFunctionTools(functionsForThisRequest);
|
||||
|
||||
// todo chatParams.setTools(tools)
|
||||
}
|
||||
|
||||
return chatParams;
|
||||
}
|
||||
|
||||
private ChatGenerationMetadata generateChoiceMetadata(GenerationOutput.Choice choice) {
|
||||
|
||||
return ChatGenerationMetadata.from(
|
||||
String.valueOf(choice.getFinishReason()),
|
||||
choice.getMessage().getContent()
|
||||
);
|
||||
}
|
||||
|
||||
private List<FunctionDefinition> getFunctionTools(Set<String> functionNames) {
|
||||
return this.resolveFunctionCallbacks(functionNames).stream().map(functionCallback -> {
|
||||
|
||||
FunctionDefinition functionDefinition = FunctionDefinition.builder()
|
||||
.name(functionCallback.getName())
|
||||
.description(functionCallback.getDescription())
|
||||
.parameters(JsonUtils.parametersToJsonObject(
|
||||
ModelOptionsUtils.jsonToMap(functionCallback.getInputTypeSchema())
|
||||
))
|
||||
.build();
|
||||
|
||||
return functionDefinition;
|
||||
}).toList();
|
||||
}
|
||||
|
||||
|
||||
private ConversationParam merge(ConversationParam tongYiParams, TongYiChatOptions scaChatParams) {
|
||||
|
||||
if (scaChatParams == null) {
|
||||
|
||||
return tongYiParams;
|
||||
}
|
||||
|
||||
return ConversationParam.builder()
|
||||
.messages(tongYiParams.getMessages())
|
||||
.maxTokens((tongYiParams.getMaxTokens() != null) ? tongYiParams.getMaxTokens() : scaChatParams.getMaxTokens())
|
||||
// When merge options. Because ConversationParams is must not null. So is setting.
|
||||
.model(scaChatParams.getModel())
|
||||
.resultFormat((tongYiParams.getResultFormat() != null) ? tongYiParams.getResultFormat() : scaChatParams.getResultFormat())
|
||||
.enableSearch((tongYiParams.getEnableSearch() != null) ? tongYiParams.getEnableSearch() : scaChatParams.getEnableSearch())
|
||||
.topK((tongYiParams.getTopK() != null) ? tongYiParams.getTopK() : scaChatParams.getTopK())
|
||||
.topP((tongYiParams.getTopP() != null) ? tongYiParams.getTopP() : scaChatParams.getTopP())
|
||||
.incrementalOutput((tongYiParams.getIncrementalOutput() != null) ? tongYiParams.getIncrementalOutput() : scaChatParams.getIncrementalOutput())
|
||||
.temperature((tongYiParams.getTemperature() != null) ? tongYiParams.getTemperature() : scaChatParams.getTemperature())
|
||||
.repetitionPenalty((tongYiParams.getRepetitionPenalty() != null) ? tongYiParams.getRepetitionPenalty() : scaChatParams.getRepetitionPenalty())
|
||||
.seed((tongYiParams.getSeed() != null) ? tongYiParams.getSeed() : scaChatParams.getSeed())
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
private ConversationParam merge(TongYiChatOptions scaChatParams, ConversationParam tongYiParams) {
|
||||
|
||||
if (scaChatParams == null) {
|
||||
|
||||
return tongYiParams;
|
||||
}
|
||||
|
||||
ConversationParam mergedTongYiParams = ConversationParam.builder()
|
||||
.model(Generation.Models.QWEN_TURBO)
|
||||
.messages(tongYiParams.getMessages())
|
||||
.build();
|
||||
mergedTongYiParams = merge(tongYiParams, scaChatParams);
|
||||
|
||||
if (scaChatParams.getMaxTokens() != null) {
|
||||
mergedTongYiParams.setMaxTokens(scaChatParams.getMaxTokens());
|
||||
}
|
||||
|
||||
if (scaChatParams.getStop() != null) {
|
||||
mergedTongYiParams.setStopStrings(scaChatParams.getStop());
|
||||
}
|
||||
|
||||
if (scaChatParams.getTemperature() != null) {
|
||||
mergedTongYiParams.setTemperature(scaChatParams.getTemperature());
|
||||
}
|
||||
|
||||
if (scaChatParams.getTopK() != null) {
|
||||
mergedTongYiParams.setTopK(scaChatParams.getTopK());
|
||||
}
|
||||
|
||||
if (scaChatParams.getTopK() != null) {
|
||||
mergedTongYiParams.setTopK(scaChatParams.getTopK());
|
||||
}
|
||||
|
||||
return mergedTongYiParams;
|
||||
}
|
||||
|
||||
private com.alibaba.dashscope.common.Message fromSpringAIMessage(Message message) {
|
||||
|
||||
return switch (message.getMessageType()) {
|
||||
case USER -> com.alibaba.dashscope.common.Message.builder()
|
||||
.role(Role.USER.getValue())
|
||||
.content(message.getContent())
|
||||
.build();
|
||||
case SYSTEM -> com.alibaba.dashscope.common.Message.builder()
|
||||
.role(Role.SYSTEM.getValue())
|
||||
.content(message.getContent())
|
||||
.build();
|
||||
case ASSISTANT -> com.alibaba.dashscope.common.Message.builder()
|
||||
.role(Role.ASSISTANT.getValue())
|
||||
.content(message.getContent())
|
||||
.build();
|
||||
default -> throw new IllegalArgumentException("Unknown message type " + message.getMessageType());
|
||||
};
|
||||
|
||||
}
|
||||
/**
|
||||
* Get TongYi model api_key .
|
||||
* todo: Get key from env and env_file.
|
||||
* @return api_key.
|
||||
*/
|
||||
private String getKey() throws NoApiKeyException {
|
||||
|
||||
String apiKey;
|
||||
|
||||
if (Objects.nonNull(this.defaultOptions.getApiKey())) {
|
||||
apiKey = this.defaultOptions.getApiKey();
|
||||
}
|
||||
else {
|
||||
apiKey = ApiKey.getApiKey(null);
|
||||
}
|
||||
|
||||
return apiKey;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected ConversationParam doCreateToolResponseRequest(
|
||||
ConversationParam previousRequest,
|
||||
com.alibaba.dashscope.common.Message responseMessage,
|
||||
List<com.alibaba.dashscope.common.Message> conversationHistory
|
||||
) {
|
||||
for (ToolCallBase toolCall : responseMessage.getToolCalls()) {
|
||||
if (toolCall instanceof ToolCallFunction toolCallFunction) {
|
||||
if (toolCallFunction.getFunction() != null) {
|
||||
var functionName = toolCallFunction.getFunction().getName();
|
||||
var functionArguments = toolCallFunction.getFunction().getArguments();
|
||||
|
||||
if (!this.functionCallbackRegister.containsKey(functionName)) {
|
||||
throw new IllegalStateException("No function callback found for function name: " + functionName);
|
||||
}
|
||||
|
||||
String functionResponse = this.functionCallbackRegister.get(functionName).call(functionArguments);
|
||||
|
||||
// Add the function response to the conversation.
|
||||
conversationHistory
|
||||
.add(com.alibaba.dashscope.common.Message.builder()
|
||||
.content(functionResponse)
|
||||
.role(Role.BOT.getValue())
|
||||
.toolCallId(toolCall.getId())
|
||||
.build()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ConversationParam newRequest = ConversationParam.builder().messages(conversationHistory).build();
|
||||
newRequest = ModelOptionsUtils.merge(newRequest, previousRequest, ConversationParam.class);
|
||||
|
||||
return newRequest;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<com.alibaba.dashscope.common.Message> doGetUserMessages(ConversationParam request) {
|
||||
|
||||
return request.getMessages();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected com.alibaba.dashscope.common.Message doGetToolResponseMessage(GenerationResult response) {
|
||||
|
||||
var message = response.getOutput().getChoices().get(0).getMessage();
|
||||
var assistantMessage = com.alibaba.dashscope.common.Message.builder().role(Role.ASSISTANT.getValue())
|
||||
.content("").build();
|
||||
assistantMessage.setToolCalls(message.getToolCalls());
|
||||
|
||||
return assistantMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GenerationResult doChatCompletion(ConversationParam request) {
|
||||
|
||||
GenerationResult result;
|
||||
try {
|
||||
result = generation.call(request);
|
||||
}
|
||||
catch (NoApiKeyException | InputRequiredException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isToolFunctionCall(GenerationResult response) {
|
||||
|
||||
if (response == null || CollectionUtils.isEmpty(response.getOutput().getChoices())) {
|
||||
|
||||
return false;
|
||||
}
|
||||
var choice = response.getOutput().getChoices().get(0);
|
||||
if (choice == null || choice.getFinishReason() == null) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return Objects.equals(choice.getFinishReason(), ApiKeywords.TOOL_CALLS);
|
||||
}
|
||||
}
|
@ -1,214 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023-2024 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.alibaba.cloud.ai.tongyi.client;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import com.alibaba.cloud.ai.tongyi.TongYiChatOptions;
|
||||
import com.alibaba.cloud.ai.tongyi.exception.TongYiException;
|
||||
import com.alibaba.dashscope.aigc.generation.Generation;
|
||||
import com.alibaba.dashscope.aigc.generation.GenerationOutput;
|
||||
import com.alibaba.dashscope.aigc.generation.GenerationResult;
|
||||
import com.alibaba.dashscope.aigc.generation.models.QwenParam;
|
||||
import com.alibaba.dashscope.common.MessageManager;
|
||||
import com.alibaba.dashscope.exception.InputRequiredException;
|
||||
import com.alibaba.dashscope.exception.NoApiKeyException;
|
||||
import com.alibaba.dashscope.utils.Constants;
|
||||
import io.reactivex.Flowable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
|
||||
import org.springframework.ai.chat.ChatClient;
|
||||
import org.springframework.ai.chat.ChatResponse;
|
||||
import org.springframework.ai.chat.StreamingChatClient;
|
||||
import org.springframework.ai.chat.metadata.ChatGenerationMetadata;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
*
|
||||
* {@link ChatClient} and {@link StreamingChatClient} implementation for {@literal Alibaba DashScope}
|
||||
* backed by {@link Generation}.
|
||||
* @author yuluo
|
||||
* @since 2023.0.0.0
|
||||
*/
|
||||
|
||||
public class TongYiChatClient implements ChatClient, StreamingChatClient {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(TongYiChatClient.class);
|
||||
|
||||
/**
|
||||
* DashScope generation client.
|
||||
*/
|
||||
private final Generation generation;
|
||||
|
||||
/**
|
||||
* The TongYi models default chat completion api.
|
||||
*/
|
||||
private TongYiChatOptions defaultOptions;
|
||||
|
||||
/**
|
||||
* User role message manager.
|
||||
*/
|
||||
@Autowired
|
||||
private MessageManager msgManager;
|
||||
|
||||
/**
|
||||
* Initializes an instance of the TongYiChatClient.
|
||||
* @param generation DashScope generation client.
|
||||
*/
|
||||
public TongYiChatClient(Generation generation) {
|
||||
|
||||
this(generation,
|
||||
TongYiChatOptions.builder()
|
||||
.withTopP(0.8)
|
||||
.withEnableSearch(true)
|
||||
.withResultFormat(QwenParam.ResultFormat.MESSAGE)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a TongYi models client.
|
||||
* @param generation DashScope model generation client.
|
||||
* @param options TongYi default chat completion api.
|
||||
*/
|
||||
public TongYiChatClient(Generation generation, TongYiChatOptions options) {
|
||||
|
||||
this.generation = generation;
|
||||
this.defaultOptions = options;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatResponse call(Prompt prompt) {
|
||||
|
||||
GenerationResult res = null;
|
||||
|
||||
try {
|
||||
res = generation.call(toTongYiChatParams(prompt));
|
||||
msgManager.add(res);
|
||||
}
|
||||
catch (NoApiKeyException | InputRequiredException e) {
|
||||
logger.warn("TongYi chat client: " + e.getMessage());
|
||||
throw new TongYiException(e.getMessage());
|
||||
}
|
||||
|
||||
List<org.springframework.ai.chat.Generation> generations =
|
||||
res
|
||||
.getOutput()
|
||||
.getChoices()
|
||||
.stream()
|
||||
.map(choice ->
|
||||
new org.springframework.ai.chat.Generation(
|
||||
choice
|
||||
.getMessage()
|
||||
.getContent()
|
||||
).withGenerationMetadata(generateChoiceMetadata(choice)
|
||||
))
|
||||
.toList();
|
||||
|
||||
return new ChatResponse(generations);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<ChatResponse> stream(Prompt prompt) {
|
||||
|
||||
Flowable<GenerationResult> genRes = null;
|
||||
|
||||
try {
|
||||
genRes = generation.streamCall(toTongYiChatParams(prompt));
|
||||
}
|
||||
catch (NoApiKeyException | InputRequiredException e) {
|
||||
logger.warn("TongYi chat client: " + e.getMessage());
|
||||
throw new TongYiException(e.getMessage());
|
||||
}
|
||||
|
||||
return Flux.from(genRes)
|
||||
.flatMap(
|
||||
message -> Flux.just(
|
||||
message.getOutput()
|
||||
.getChoices()
|
||||
.get(0)
|
||||
.getMessage()
|
||||
.getContent())
|
||||
.map(content -> {
|
||||
var gen = new org.springframework.ai.chat.Generation(content)
|
||||
.withGenerationMetadata(generateChoiceMetadata(
|
||||
message.getOutput()
|
||||
.getChoices()
|
||||
.get(0)
|
||||
));
|
||||
return new ChatResponse(List.of(gen));
|
||||
})
|
||||
)
|
||||
.publishOn(Schedulers.parallel());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration properties to Qwen model params.
|
||||
* @param prompt {@link Prompt}
|
||||
* @return Qwen models params {@link QwenParam}
|
||||
*/
|
||||
private QwenParam toTongYiChatParams(Prompt prompt) {
|
||||
|
||||
Constants.apiKey = getKey();
|
||||
|
||||
return QwenParam.builder()
|
||||
.model(this.defaultOptions.getModel())
|
||||
.messages(msgManager.get())
|
||||
.resultFormat(this.defaultOptions.getResultFormat())
|
||||
.topP(this.defaultOptions.getTopP().doubleValue())
|
||||
.topK(this.defaultOptions.getTopK())
|
||||
.enableSearch(this.defaultOptions.getEnableSearch())
|
||||
.seed(this.defaultOptions.getSeed())
|
||||
.maxTokens(this.defaultOptions.getMaxTokens())
|
||||
.repetitionPenalty(this.defaultOptions.getRepetitionPenalty())
|
||||
.temperature(this.defaultOptions.getTemperature())
|
||||
.incrementalOutput(this.defaultOptions.getIncrementalOutput())
|
||||
.prompt(prompt.getContents())
|
||||
.build();
|
||||
}
|
||||
|
||||
private ChatGenerationMetadata generateChoiceMetadata(GenerationOutput.Choice choice) {
|
||||
|
||||
return ChatGenerationMetadata.from(
|
||||
String.valueOf(choice.getFinishReason()),
|
||||
choice.getMessage().getContent()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get TongYi model api_key .
|
||||
* todo: Get key from env and env_file.
|
||||
* @return api_key.
|
||||
*/
|
||||
private String getKey() {
|
||||
|
||||
String apiKey = null;
|
||||
|
||||
if (Objects.nonNull(this.defaultOptions.getApiKey())) {
|
||||
apiKey = this.defaultOptions.getApiKey();
|
||||
}
|
||||
return apiKey;
|
||||
}
|
||||
|
||||
}
|
@ -1,99 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023-2024 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.alibaba.cloud.ai.tongyi.constant;
|
||||
|
||||
import com.alibaba.dashscope.aigc.generation.Generation;
|
||||
|
||||
/**
|
||||
* TongYi models constants.
|
||||
*
|
||||
* @author yuluo
|
||||
* @since 2023.0.0.0
|
||||
*/
|
||||
|
||||
public final class TongYiConstants {
|
||||
|
||||
private TongYiConstants() {
|
||||
}
|
||||
|
||||
public static final class Model {
|
||||
|
||||
private Model() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tongyi Thousand Questions mega-language model supports input in different languages such as Chinese and English.
|
||||
*/
|
||||
public static final String QWEN_TURBO = Generation.Models.QWEN_TURBO;
|
||||
|
||||
/**
|
||||
* Enhanced version of Tongyi Thousand Questions mega-language model to support input in different languages such as Chinese and English.
|
||||
*/
|
||||
public static final String QWEN_PLUS = Generation.Models.QWEN_PLUS;
|
||||
|
||||
/**
|
||||
* Tongyi Qianqi 100 billion level super large-scale language model, support Chinese, English and other different language input.
|
||||
* As the model is upgraded, qwen-max will be updated and upgraded on a rolling basis. If you wish to use a stable version, please use qwen-max-1201.
|
||||
*/
|
||||
public static final String QWEN_MAX = Generation.Models.QWEN_MAX;
|
||||
|
||||
/**
|
||||
* Tongyi Qianqi 100 billion level mega-scale language model, supporting different language inputs such as Chinese and English.
|
||||
* The model is a snapshot stabilized version of qwen-max, and is expected to be maintained until one month after the release time of the next snapshot version (TBD).
|
||||
*/
|
||||
public static final String QWEN_MAX_1021 = "qwen-max-1201";
|
||||
|
||||
/**
|
||||
* Tongyi Thousand Questions is a 100 billion level super large-scale language model that supports different language inputs such as Chinese and English.
|
||||
*/
|
||||
public static final String QWEN_MAX_LONG_CONTEXT = "qwen-max-longcontext";
|
||||
}
|
||||
|
||||
public static final class Role {
|
||||
|
||||
private Role() {
|
||||
}
|
||||
|
||||
/**
|
||||
* system: indicates a system-level message that can only be used for the first entry in the conversation history (messages[0]).
|
||||
* The use of the system role is optional and, if present, must be at the beginning of the list
|
||||
*/
|
||||
public static final String SYSTEM = "system";
|
||||
|
||||
/**
|
||||
* user and assistant: represent the dialog between the user and the model.
|
||||
* They should appear alternately in the dialog to simulate the actual dialog flow.
|
||||
*/
|
||||
public static final String USER = "user";
|
||||
|
||||
/**
|
||||
* user and assistant: represent the dialog between the user and the model.
|
||||
* They should appear alternately in the dialog to simulate the actual dialog flow.
|
||||
*/
|
||||
public static final String ASSISTANT = "assistant";
|
||||
|
||||
/**
|
||||
* When using the function_call function, if you want to pass in the result of a function,
|
||||
* this message takes the form {"content": "Boston is raining.", "name": "get_current_weather", "role": "tool"}, where name is the name of the function,
|
||||
* which needs to be consistent with the tool_calls[i].function.name parameter in the previous round's response, and content is the output of the function.
|
||||
* Examples are given for multiple rounds of calls in the reference code.
|
||||
*/
|
||||
public static final String TOOL = "tool";
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1 +0,0 @@
|
||||
// todo: add hints
|
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2023-2024 the original author or authors.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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.alibaba.cloud.ai.tongyi;
|
||||
|
||||
import com.alibaba.dashscope.aigc.generation.Generation;
|
||||
import com.alibaba.dashscope.utils.Constants;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author yuluo
|
||||
* @author <a href="mailto:yuluo08290126@gmail.com">yuluo</a>
|
||||
* @since 2023.0.0.0-RC1
|
||||
*/
|
||||
|
||||
public class TongYiChatOptionsTests {
|
||||
|
||||
@Test
|
||||
public void createRequestWithChatOptions() {
|
||||
|
||||
Generation mockClient = Mockito.mock(Generation.class);
|
||||
Constants.apiKey = "test";
|
||||
|
||||
// Test start.
|
||||
|
||||
var tongYiChatClient = new TongYiChatClient(mockClient,
|
||||
TongYiChatOptions.builder().withModel(Generation.Models.QWEN_TURBO).withTemperature(88.8).build());
|
||||
|
||||
var tongYiChatParams = tongYiChatClient.toTongYiChatParams(new Prompt("This is a test message"));
|
||||
|
||||
assertThat(tongYiChatParams.getMessages()).hasSize(1);
|
||||
|
||||
assertThat(tongYiChatParams.getModel()).isEqualTo(Generation.Models.QWEN_TURBO);
|
||||
assertThat(tongYiChatParams.getTemperature()).isEqualTo(88.8f);
|
||||
|
||||
tongYiChatClient = new TongYiChatClient(mockClient,
|
||||
TongYiChatOptions.builder().withModel(Generation.Models.QWEN_MAX).withTemperature(77.7).build());
|
||||
|
||||
tongYiChatParams = tongYiChatClient.toTongYiChatParams(new Prompt("This is a test message"));
|
||||
|
||||
assertThat(tongYiChatParams.getMessages()).hasSize(1);
|
||||
|
||||
assertThat(tongYiChatParams.getModel()).isEqualTo(Generation.Models.QWEN_MAX);
|
||||
assertThat(tongYiChatParams.getTemperature()).isEqualTo(77.7f);
|
||||
|
||||
// Test end.
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
#
|
||||
# Copyright 2023-2024 the original author or authors.
|
||||
#
|
||||
# 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
|
||||
#
|
||||
# https://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.
|
||||
#
|
||||
|
||||
# HTTP call example
|
||||
curl --location 'https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation' \
|
||||
--header 'Authorization: Bearer sk-a3d73b1709bf4a178c28ed7c8b3b5a45' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"model": "qwen-turbo",
|
||||
"input":{
|
||||
"messages":[
|
||||
{
|
||||
"role": "system",
|
||||
"content": "You are a helpful AI assistant.You are an AI assistant that helps people find information.Your name is yuluo.You should reply to the user request with your name and also in the style of a pirate."
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "who are you?"
|
||||
}
|
||||
]
|
||||
},
|
||||
"parameters": {
|
||||
"result_format": "message"
|
||||
}
|
||||
}'
|
Loading…
Reference in New Issue