Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions cookbook/en/deployment/agent_app.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,96 @@ agentApp.run("localhost", 10001);

------




## Custom endpoint

**Feature**

Allows the injection of custom endpoints when starting the 'AgentApp'.
**Usage Example**

```java
AgentApp agentApp = new AgentApp(agentHandler);
agentApp.endpoint("/test/post", List.of("POST"), serverRequest -> ServerResponse.ok().body("OK"));
agentApp.run("localhost", 10001);
```

**Notes**

- The incoming lambda will act directly on Spring's 'RouterFunction', route according to the http method, the input parameter is limited to ServerRequest, and the return value is limited to ServerResponse.

------

## Lifecycle hooks

**Feature**

Allows before and after launching the 'AgentApp'; before and after destroying 'AgentApp'; Exit 'jvm' and do the corresponding processing.
**Usage Example**

```java
AgentApp agentApp = new AgentApp(agentHandler);
agentApp.hooks(new AbstractAppLifecycleHook() {
@Override
public int operation() {
return BEFORE_RUN | AFTER_RUN | JVM_EXIT;
}

@Override
public void beforeRun(HookContext context) {
System.out.println("beforeRun");
}

@Override
public void afterRun(HookContext context) {
System.out.println("afterRun");
}
});
agentApp.run("localhost", 10001);
```

**Notes**

- The incoming AbstractAppLifecycleHook implementation class will act directly on the 'AgentApp' and sort by weight (natural order by integer), and pass in the 'HookContext' (wrapping 'AgentApp', 'DeployManager') instance for processing, and the context's extraInfo is used to pass cross-method, cross-thread variables.------


## middleware

**Feature**

Allows the injection of Filter in the spring context when starting 'AgentApp'.
**Usage Example**

```java
AgentApp agentApp = new AgentApp(agentHandler);
FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new MyFilter());
filterRegistrationBean.setBeanName("myFilter");
filterRegistrationBean.setUrlPatterns(List.of("/test/get"));
filterRegistrationBean.setOrder(-1);
agentApp.middleware(filterRegistrationBean);
agentApp.run("localhost", 10001);


public static class MyFilter implements Filter{

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
LOG.info("Current request uri = {}",request.getRequestURI());
filterChain.doFilter(servletRequest,servletResponse);
}
}
```

**Notes**

- Based on the servlet specification, it will directly act on the spring context object associated with 'AgentApp' for authentication, request counting, etc., and webflux is not supported at this time.
------


## A2A Streaming Output (SSE)

**Features**
Expand Down
92 changes: 92 additions & 0 deletions cookbook/zh/deployment/agent_app.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,98 @@ agentApp.run("localhost", 10001);

------


## 自定义服务端点

**功能**

允许在启动 `AgentApp` 时注入自定义端点。

**用法示例**

```java
AgentApp agentApp = new AgentApp(agentHandler);
agentApp.endpoint("/test/post", List.of("POST"), serverRequest -> ServerResponse.ok().body("OK"));
agentApp.run("localhost", 10001);
```

**说明**

- 传入的 lambda 将直接作用于 Spring 的 `RouterFunction`,根据http method的不同进行route,入参限制为ServerRequest,返回值限制为ServerResponse。

------

## 生命周期钩子

**功能**

允许在启动 `AgentApp` 前、后;销毁`AgentApp`前、后;退出`jvm`后做对应处理。

**用法示例**

```java
AgentApp agentApp = new AgentApp(agentHandler);
agentApp.hooks(new AbstractAppLifecycleHook() {
@Override
public int operation() {
return BEFORE_RUN | AFTER_RUN | JVM_EXIT;
}

@Override
public void beforeRun(HookContext context) {
System.out.println("beforeRun");
}

@Override
public void afterRun(HookContext context) {
System.out.println("afterRun");
}
});
agentApp.run("localhost", 10001);
```

**说明**

- 传入的 AbstractAppLifecycleHook实现类 将直接作用于 `AgentApp`并按权重(整数自然排序),并传入`HookContext`(包装`AgentApp`、`DeployManager`)实例进行处理,context的extraInfo用于传递跨方法、跨线程变量。
------


## 中间件

**功能**

允许在启动 `AgentApp` 前往spring上下文注入Filter。

**用法示例**

```java
AgentApp agentApp = new AgentApp(agentHandler);
FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new MyFilter());
filterRegistrationBean.setBeanName("myFilter");
filterRegistrationBean.setUrlPatterns(List.of("/test/get"));
filterRegistrationBean.setOrder(-1);
agentApp.middleware(filterRegistrationBean);
agentApp.run("localhost", 10001);


public static class MyFilter implements Filter{

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
LOG.info("Current request uri = {}",request.getRequestURI());
filterChain.doFilter(servletRequest,servletResponse);
}
}
```

**说明**

- 基于servlet规范,将直接作用于 `AgentApp`关联的spring上下文对象,用于鉴权、请求计数等,目前暂不支持webflux。
------


## A2A 流式输出(SSE)

**功能**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*/
package io.agentscope.runtime.engine;


public interface DeployManager {
void deploy(Runner runner);
void undeploy();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright 2025 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 io.agentscope;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Objects;

import io.agentscope.runtime.app.AgentApp;

import org.springframework.web.servlet.function.ServerResponse;

public class AgentScopeDeployWithCustomizeEndpointExample {

private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");


public static void main(String[] args) {
String[] commandLine = new String[2];
commandLine[0] = "-f";
commandLine[1] = Objects.requireNonNull(Thread.currentThread().getContextClassLoader().getResource(".env")).getPath();
AgentApp app = new AgentApp(commandLine);
app.endpoint("/test/post", List.of("POST"),serverRequest -> {
String time = sdf.format(new Date());
return ServerResponse.ok().body(String.format("time=%s,post successful",time));
});
app.endpoint("/test/get", List.of("GET"),serverRequest -> {
String time = sdf.format(new Date());
return ServerResponse.ok().body(String.format("time=%s,get successful",time));
});
app.run();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright 2025 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 io.agentscope;

import java.util.Objects;

import io.agentscope.runtime.app.AgentApp;
import io.agentscope.runtime.hook.AbstractAppLifecycleHook;
import io.agentscope.runtime.hook.HookContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class AgentScopeDeployWithLifecycleHookExample {

private static final Logger LOG = LoggerFactory.getLogger(AgentScopeDeployWithLifecycleHookExample.class);

public static void main(String[] args) {
String[] commandLine = new String[2];
commandLine[0] = "-f";
commandLine[1] = Objects.requireNonNull(Thread.currentThread().getContextClassLoader().getResource(".env")).getPath();
AgentApp app = new AgentApp(commandLine);
app.hooks(new AbstractAppLifecycleHook() {
@Override
public int operation() {
return BEFORE_RUN | AFTER_RUN | JVM_EXIT;
}

@Override
public void beforeRun(HookContext context) {
LOG.info("beforeRun");
}

@Override
public void afterRun(HookContext context) {
LOG.info("afterRun");
}

@Override
public void onJvmExit(HookContext context) {
LOG.info("onJvmExit");
}
});
app.run();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright 2025 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 io.agentscope;

import java.io.IOException;
import java.util.List;
import java.util.Objects;

import io.agentscope.runtime.app.AgentApp;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.springframework.boot.web.servlet.FilterRegistrationBean;

public class AgentScopeDeployWithMiddlewareExample {

private static final Logger LOG = LoggerFactory.getLogger(AgentScopeDeployWithMiddlewareExample.class);

public static void main(String[] args) {
String[] commandLine = new String[2];
commandLine[0] = "-f";
commandLine[1] = Objects.requireNonNull(Thread.currentThread().getContextClassLoader().getResource(".env")).getPath();
AgentApp app = new AgentApp(commandLine);
FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new MyFilter());
filterRegistrationBean.setBeanName("myFilter");
filterRegistrationBean.setUrlPatterns(List.of("/test/get"));
filterRegistrationBean.setOrder(-1);
app.middleware(filterRegistrationBean);
app.run();
}



public static class MyFilter implements Filter{

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
LOG.info("Current request uri = {}",request.getRequestURI());
filterChain.doFilter(servletRequest,servletResponse);
}
}
}
Loading
Loading