diff --git a/forest-examples/example-orcarouter/README.md b/forest-examples/example-orcarouter/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..4be789198ae2ca87e69730417cb464788e61b73f
--- /dev/null
+++ b/forest-examples/example-orcarouter/README.md
@@ -0,0 +1,31 @@
+
+Spring Boot 环境下接入 [OrcaRouter](https://www.orcarouter.ai) 的示例
+====
+
+本案例使用`forest-spring-boot-starter`包发送HTTP请求来调用 [OrcaRouter](https://www.orcarouter.ai) 的接口
+
+[OrcaRouter](https://www.orcarouter.ai) 是一个 OpenAI 兼容的 LLM 路由网关,通过统一的 `https://api.orcarouter.ai/v1` 接入点路由到多个上游模型。
+
+使用 Fastjson2 进行JSON序列化/反序列化
+
+#### 项目结构介绍
+
+本案例包含以下几个类:
+
+- OrcaRouterExampleApplication: 项目启动类
+- OrcaRouter: 接入 OrcaRouter 的 Forest 声明式接口
+- OrcaRouterResult: 用于接受 OrcaRouter 响应结果的数据类
+- OrcaRouterResultChoice: 用于接受 OrcaRouter 响应结果中文本内容的数据类
+- OrcaRouterResultDelta: 用于接受 OrcaRouter 增量更新内容的类
+
+#### 配置
+
+```yaml
+forest:
+ connect-timeout: 10000 # HTTP请求连接超时时间
+ read-timeout: 3600000 # HTTP请求读取超时时间
+ variables: # 自定义变量:
+ baseUrl: https://api.orcarouter.ai/v1
+ apiKey: YOUR_API_KEY # 你的 OrcaRouter 的 API KEY (sk-orca- 开头)
+ model: orcarouter/auto # 由 OrcaRouter 自动路由到最佳模型
+```
diff --git a/forest-examples/example-orcarouter/pom.xml b/forest-examples/example-orcarouter/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..55fcc1cefc59992f21768fd9f544cf27a7b1838f
--- /dev/null
+++ b/forest-examples/example-orcarouter/pom.xml
@@ -0,0 +1,88 @@
+
+
+ 4.0.0
+
+ com.dtflys.forest
+ example-orcarouter
+ 1.0.0
+
+
+ 1.8
+ true
+ 1.7.2
+ 2.0.53
+ 1.18.30
+
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.1.8.RELEASE
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+ com.dtflys.forest
+ forest-spring-boot-starter
+ ${forest.version}
+
+
+
+ com.dtflys.forest
+ forest-jaxb
+ ${forest.version}
+
+
+
+ com.alibaba.fastjson2
+ fastjson2
+ ${fastjson2.version}
+
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.junit.vintage
+ junit-vintage-engine
+
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+
+
+
+
+
+
diff --git a/forest-examples/example-orcarouter/src/main/java/com/dtflys/forest/example/OrcaRouterExampleApplication.java b/forest-examples/example-orcarouter/src/main/java/com/dtflys/forest/example/OrcaRouterExampleApplication.java
new file mode 100644
index 0000000000000000000000000000000000000000..6162df89bdf711ca3be7e4bde6f93f9aee712af3
--- /dev/null
+++ b/forest-examples/example-orcarouter/src/main/java/com/dtflys/forest/example/OrcaRouterExampleApplication.java
@@ -0,0 +1,50 @@
+package com.dtflys.forest.example;
+
+import com.dtflys.forest.example.client.OrcaRouter;
+import com.dtflys.forest.example.model.OrcaRouterResult;
+import com.dtflys.forest.sse.SSELinesMode;
+import com.dtflys.forest.utils.StringUtils;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+import javax.annotation.Resource;
+
+@SpringBootApplication
+public class OrcaRouterExampleApplication implements CommandLineRunner {
+
+ @Resource
+ private OrcaRouter orcaRouter;
+
+ /**
+ * 通过 OrcaRouter 路由网关发送聊天请求
+ *
+ * @param message 发送给 OrcaRouter 的消息
+ */
+ private void completions(String message) {
+ orcaRouter.completions(message)
+ .setOnMessage(event -> {
+ try {
+ OrcaRouterResult result = event.value(OrcaRouterResult.class);
+ String content = result.content();
+ System.out.print(StringUtils.isNotBlank(content) ? content : "");
+ } catch (Exception e) {
+ }
+ })
+ .listen(SSELinesMode.SINGLE_LINE);
+ }
+
+ @Override
+ public void run(String... args) {
+ completions("1+1等于几?");
+ }
+
+ public static void main(String[] args) {
+ try {
+ SpringApplication.run(OrcaRouterExampleApplication.class, args);
+ } catch (Throwable th) {
+ th.printStackTrace();
+ }
+ }
+
+}
diff --git a/forest-examples/example-orcarouter/src/main/java/com/dtflys/forest/example/client/OrcaRouter.java b/forest-examples/example-orcarouter/src/main/java/com/dtflys/forest/example/client/OrcaRouter.java
new file mode 100644
index 0000000000000000000000000000000000000000..f529138388170c1d5ea1534fee48759199e66bb0
--- /dev/null
+++ b/forest-examples/example-orcarouter/src/main/java/com/dtflys/forest/example/client/OrcaRouter.java
@@ -0,0 +1,18 @@
+package com.dtflys.forest.example.client;
+
+import com.dtflys.forest.annotation.BaseRequest;
+import com.dtflys.forest.annotation.Post;
+import com.dtflys.forest.annotation.Var;
+import com.dtflys.forest.example.interceptor.OrcaRouterInterceptor;
+import com.dtflys.forest.http.ForestSSE;
+
+@BaseRequest(baseURL = "{baseUrl}", interceptor = OrcaRouterInterceptor.class)
+public interface OrcaRouter {
+
+ @Post(
+ url = "/chat/completions",
+ contentType = "application/json",
+ headers = "Authorization: Bearer {apiKey}",
+ data = "{\"messages\":[{\"content\":\"{content}\",\"role\":\"user\"}],\"model\":\"{model}\",\"stream\":true}")
+ ForestSSE completions(@Var("content") String content);
+}
diff --git a/forest-examples/example-orcarouter/src/main/java/com/dtflys/forest/example/interceptor/OrcaRouterInterceptor.java b/forest-examples/example-orcarouter/src/main/java/com/dtflys/forest/example/interceptor/OrcaRouterInterceptor.java
new file mode 100644
index 0000000000000000000000000000000000000000..41d371a7323cf76300d2e2228306e5063afad193
--- /dev/null
+++ b/forest-examples/example-orcarouter/src/main/java/com/dtflys/forest/example/interceptor/OrcaRouterInterceptor.java
@@ -0,0 +1,19 @@
+package com.dtflys.forest.example.interceptor;
+
+import com.dtflys.forest.http.ForestRequest;
+import com.dtflys.forest.http.ForestResponse;
+import com.dtflys.forest.interceptor.ResponseResult;
+import com.dtflys.forest.interceptor.SSEInterceptor;
+
+public class OrcaRouterInterceptor implements SSEInterceptor {
+
+ @Override
+ public ResponseResult onResponse(ForestRequest request, ForestResponse response) {
+ if (response.isError()) {
+ System.out.println("服务端繁忙,请稍后再试");
+ return success();
+ }
+ return proceed();
+ }
+
+}
diff --git a/forest-examples/example-orcarouter/src/main/java/com/dtflys/forest/example/model/OrcaRouterResult.java b/forest-examples/example-orcarouter/src/main/java/com/dtflys/forest/example/model/OrcaRouterResult.java
new file mode 100644
index 0000000000000000000000000000000000000000..be104e676b37c8683ba3e38c29e40e8cab3e3226
--- /dev/null
+++ b/forest-examples/example-orcarouter/src/main/java/com/dtflys/forest/example/model/OrcaRouterResult.java
@@ -0,0 +1,35 @@
+package com.dtflys.forest.example.model;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.alibaba.fastjson2.annotation.JSONField;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class OrcaRouterResult {
+
+ private String id;
+
+ private String object;
+
+ private Integer created;
+
+ private String model;
+
+ @JSONField(name = "system_fingerprint")
+ private String systemFingerprint;
+
+ private List choices;
+
+ public String content() {
+ List choices = getChoices();
+ if (choices != null && !choices.isEmpty()) {
+ JSONObject chooseJson = choices.get(0);
+ OrcaRouterResultChoice choice = chooseJson.toJavaObject(OrcaRouterResultChoice.class);
+ return choice.getDelta().getContent();
+ }
+ return "";
+ }
+
+}
diff --git a/forest-examples/example-orcarouter/src/main/java/com/dtflys/forest/example/model/OrcaRouterResultChoice.java b/forest-examples/example-orcarouter/src/main/java/com/dtflys/forest/example/model/OrcaRouterResultChoice.java
new file mode 100644
index 0000000000000000000000000000000000000000..d393cb3bb1094d6d872cd4739c21cfecd3443324
--- /dev/null
+++ b/forest-examples/example-orcarouter/src/main/java/com/dtflys/forest/example/model/OrcaRouterResultChoice.java
@@ -0,0 +1,11 @@
+package com.dtflys.forest.example.model;
+
+import lombok.Data;
+
+@Data
+public class OrcaRouterResultChoice {
+
+ private Integer index;
+
+ private OrcaRouterResultDelta delta;
+}
diff --git a/forest-examples/example-orcarouter/src/main/java/com/dtflys/forest/example/model/OrcaRouterResultDelta.java b/forest-examples/example-orcarouter/src/main/java/com/dtflys/forest/example/model/OrcaRouterResultDelta.java
new file mode 100644
index 0000000000000000000000000000000000000000..aa321c5aad2344f106545fa80105c0862999145e
--- /dev/null
+++ b/forest-examples/example-orcarouter/src/main/java/com/dtflys/forest/example/model/OrcaRouterResultDelta.java
@@ -0,0 +1,15 @@
+package com.dtflys.forest.example.model;
+
+import com.alibaba.fastjson2.annotation.JSONField;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class OrcaRouterResultDelta {
+
+ private String content;
+
+ @JSONField(name = "reasoning_content")
+ private String reasoningContent;
+}
diff --git a/forest-examples/example-orcarouter/src/main/resources/application.yml b/forest-examples/example-orcarouter/src/main/resources/application.yml
new file mode 100644
index 0000000000000000000000000000000000000000..a204c00b5602a43586a620ba722e202f99891e2b
--- /dev/null
+++ b/forest-examples/example-orcarouter/src/main/resources/application.yml
@@ -0,0 +1,8 @@
+
+forest:
+ connect-timeout: 10000
+ read-timeout: 3600000
+ variables:
+ baseUrl: https://api.orcarouter.ai/v1
+ apiKey: YOUR_API_KEY
+ model: orcarouter/auto