发布 v1.1.0 #2
@@ -0,0 +1,31 @@
|
|||||||
|
package com.easyagents.agent.runtime.agentscope;
|
||||||
|
|
||||||
|
import io.agentscope.core.formatter.openai.OpenAIChatFormatter;
|
||||||
|
import io.agentscope.core.formatter.openai.dto.OpenAIContentPart;
|
||||||
|
import io.agentscope.core.formatter.openai.dto.OpenAIMessage;
|
||||||
|
import io.agentscope.core.message.Msg;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 为 OpenAI-compatible 模型补充 system content 内容块数组兼容能力。
|
||||||
|
*/
|
||||||
|
public final class AgentOpenAIChatFormatter extends OpenAIChatFormatter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 AgentScope 消息转换为 OpenAI 消息,并规范 system 文本的 content 格式。
|
||||||
|
*
|
||||||
|
* @param messages AgentScope 消息
|
||||||
|
* @return OpenAI 请求消息
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected List<OpenAIMessage> doFormat(List<Msg> messages) {
|
||||||
|
List<OpenAIMessage> formattedMessages = super.doFormat(messages);
|
||||||
|
for (OpenAIMessage message : formattedMessages) {
|
||||||
|
if ("system".equals(message.getRole()) && message.getContent() instanceof String text) {
|
||||||
|
message.setContent(List.of(OpenAIContentPart.text(text)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return formattedMessages;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import com.easyagents.agent.runtime.model.AgentHttpVersionPolicy;
|
|||||||
import com.easyagents.agent.runtime.model.AgentModelFactory;
|
import com.easyagents.agent.runtime.model.AgentModelFactory;
|
||||||
import com.easyagents.agent.runtime.model.AgentModelProviderType;
|
import com.easyagents.agent.runtime.model.AgentModelProviderType;
|
||||||
import com.easyagents.agent.runtime.model.AgentModelSpec;
|
import com.easyagents.agent.runtime.model.AgentModelSpec;
|
||||||
|
import com.easyagents.agent.runtime.model.AgentSystemContentFormat;
|
||||||
import io.agentscope.core.formatter.openai.DeepSeekFormatter;
|
import io.agentscope.core.formatter.openai.DeepSeekFormatter;
|
||||||
import io.agentscope.core.formatter.openai.GLMFormatter;
|
import io.agentscope.core.formatter.openai.GLMFormatter;
|
||||||
import io.agentscope.core.model.*;
|
import io.agentscope.core.model.*;
|
||||||
@@ -153,6 +154,9 @@ public class AgentScopeModelFactory implements AgentModelFactory<Model> {
|
|||||||
.stream(Boolean.TRUE.equals(options.getStream()))
|
.stream(Boolean.TRUE.equals(options.getStream()))
|
||||||
.httpTransport(httpTransportProvider.getTransport(modelSpec.getHttpVersionPolicy(), baseUrl))
|
.httpTransport(httpTransportProvider.getTransport(modelSpec.getHttpVersionPolicy(), baseUrl))
|
||||||
.generateOptions(options);
|
.generateOptions(options);
|
||||||
|
if (modelSpec.getSystemContentFormat() == AgentSystemContentFormat.TEXT_PARTS) {
|
||||||
|
builder.formatter(new AgentOpenAIChatFormatter());
|
||||||
|
}
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ public class AgentModelSpec {
|
|||||||
private boolean supportImage;
|
private boolean supportImage;
|
||||||
private boolean supportImageBase64Only;
|
private boolean supportImageBase64Only;
|
||||||
private AgentHttpVersionPolicy httpVersionPolicy = AgentHttpVersionPolicy.AUTO;
|
private AgentHttpVersionPolicy httpVersionPolicy = AgentHttpVersionPolicy.AUTO;
|
||||||
|
private AgentSystemContentFormat systemContentFormat = AgentSystemContentFormat.STRING;
|
||||||
private Map<String, Object> metadata = new LinkedHashMap<>();
|
private Map<String, Object> metadata = new LinkedHashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -162,6 +163,26 @@ public class AgentModelSpec {
|
|||||||
this.httpVersionPolicy = httpVersionPolicy == null ? AgentHttpVersionPolicy.AUTO : httpVersionPolicy;
|
this.httpVersionPolicy = httpVersionPolicy == null ? AgentHttpVersionPolicy.AUTO : httpVersionPolicy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 OpenAI-compatible 请求中的 system content 格式。
|
||||||
|
*
|
||||||
|
* @return system content 格式
|
||||||
|
*/
|
||||||
|
public AgentSystemContentFormat getSystemContentFormat() {
|
||||||
|
return systemContentFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置 OpenAI-compatible 请求中的 system content 格式。
|
||||||
|
*
|
||||||
|
* @param systemContentFormat system content 格式
|
||||||
|
*/
|
||||||
|
public void setSystemContentFormat(AgentSystemContentFormat systemContentFormat) {
|
||||||
|
this.systemContentFormat = systemContentFormat == null
|
||||||
|
? AgentSystemContentFormat.STRING
|
||||||
|
: systemContentFormat;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取元数据。
|
* 获取元数据。
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.easyagents.agent.runtime.model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OpenAI-compatible 请求中 system 消息的 content 格式。
|
||||||
|
*/
|
||||||
|
public enum AgentSystemContentFormat {
|
||||||
|
|
||||||
|
/** 使用字符串 content,保持 OpenAI Chat Completions 的常规格式。 */
|
||||||
|
STRING,
|
||||||
|
|
||||||
|
/** 使用仅包含 text 内容块的数组 content。 */
|
||||||
|
TEXT_PARTS
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package com.easyagents.agent.runtime.agentscope;
|
||||||
|
|
||||||
|
import com.easyagents.agent.runtime.model.AgentGenerationOptions;
|
||||||
|
import com.easyagents.agent.runtime.model.AgentModelProviderType;
|
||||||
|
import com.easyagents.agent.runtime.model.AgentModelSpec;
|
||||||
|
import com.easyagents.agent.runtime.model.AgentSystemContentFormat;
|
||||||
|
import io.agentscope.core.formatter.openai.dto.OpenAIContentPart;
|
||||||
|
import io.agentscope.core.formatter.openai.dto.OpenAIMessage;
|
||||||
|
import io.agentscope.core.message.Msg;
|
||||||
|
import io.agentscope.core.message.MsgRole;
|
||||||
|
import io.agentscope.core.model.OpenAIChatModel;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Agent OpenAI Chat 消息格式兼容测试。
|
||||||
|
*/
|
||||||
|
public class AgentOpenAIChatFormatterTest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证 system 文本转换为 text 内容块数组,普通 user 文本保持字符串。
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void shouldConvertOnlySystemTextToContentParts() {
|
||||||
|
AgentOpenAIChatFormatter formatter = new AgentOpenAIChatFormatter();
|
||||||
|
List<OpenAIMessage> messages = formatter.format(List.of(
|
||||||
|
Msg.builder().role(MsgRole.SYSTEM).textContent("system prompt").build(),
|
||||||
|
Msg.builder().role(MsgRole.USER).textContent("hello").build()));
|
||||||
|
|
||||||
|
Assert.assertTrue(messages.get(0).getContent() instanceof List<?>);
|
||||||
|
List<OpenAIContentPart> contentParts = messages.get(0).getContentAsList();
|
||||||
|
Assert.assertEquals(1, contentParts.size());
|
||||||
|
Assert.assertEquals("text", contentParts.get(0).getType());
|
||||||
|
Assert.assertEquals("system prompt", contentParts.get(0).getText());
|
||||||
|
Assert.assertEquals("hello", messages.get(1).getContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证内容块数组策略会安装 EasyAgents 的 OpenAI Formatter。
|
||||||
|
*
|
||||||
|
* @throws Exception 反射读取 Formatter 失败时抛出
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void textPartsPolicyShouldInstallAgentOpenAIFormatter() throws Exception {
|
||||||
|
AgentModelSpec spec = new AgentModelSpec();
|
||||||
|
spec.setProviderType(AgentModelProviderType.OPENAI_COMPATIBLE);
|
||||||
|
spec.setModelName("vlm-test");
|
||||||
|
spec.setBaseUrl("http://model.example.com/v1");
|
||||||
|
spec.setApiKey("test-key");
|
||||||
|
spec.setSystemContentFormat(AgentSystemContentFormat.TEXT_PARTS);
|
||||||
|
|
||||||
|
OpenAIChatModel model = (OpenAIChatModel) new AgentScopeModelFactory()
|
||||||
|
.create(spec, new AgentGenerationOptions());
|
||||||
|
Field formatterField = OpenAIChatModel.class.getDeclaredField("formatter");
|
||||||
|
formatterField.setAccessible(true);
|
||||||
|
|
||||||
|
Assert.assertTrue(formatterField.get(model) instanceof AgentOpenAIChatFormatter);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user