-
Notifications
You must be signed in to change notification settings - Fork 74
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
08e2abb
commit bb47bb1
Showing
32 changed files
with
965 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
springAiVersion=1.0.0-SNAPSHOT | ||
springAiMcpVersion=0.2.0 | ||
otelInstrumentationVersion=2.10.0-alpha |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
# Labs: Tools | ||
|
||
Integrating with Tools, including @Tools-annotated methods and MCP Servers. | ||
|
||
## Brave | ||
|
||
The application consumes the [Brave Search API](https://api.search.brave.com). | ||
|
||
### Create an account | ||
|
||
Visit [api.search.brave.com](https://api.search.brave.com) and sign up for a new account. | ||
Then, in the Brave Search API console, navigate to _Subscriptions_ and choose a subscription plan. | ||
You can choose the "Free AI" plan to get started. | ||
|
||
### Configure API Key | ||
|
||
In the Brave Search API console, navigate to _API Keys_ and generate a new API key. | ||
Copy and securely store your API key on your machine as an environment variable. | ||
The application will use it to access the Brave Search API. | ||
|
||
```shell | ||
export BRAVE_API_KEY=<YOUR-API-KEY> | ||
``` | ||
|
||
## Ollama | ||
|
||
The application consumes models from an [Ollama](https://ollama.ai) inference server. You can either run Ollama locally on your laptop, | ||
or rely on the Testcontainers support in Spring Boot to spin up an Ollama service automatically. | ||
If you choose the first option, make sure you have Ollama installed and running on your laptop. | ||
Either way, Spring AI will take care of pulling the needed Ollama models when the application starts, | ||
if they are not available yet on your machine. | ||
|
||
## Running the application | ||
|
||
If you're using the native Ollama application, run the application as follows. | ||
|
||
```shell | ||
./gradlew bootRun | ||
``` | ||
|
||
If you want to rely on the native Testcontainers support in Spring Boot to spin up an Ollama service at startup time, | ||
run the application as follows. | ||
|
||
```shell | ||
./gradlew bootTestRun | ||
``` | ||
|
||
## Calling the application | ||
|
||
> [!NOTE] | ||
> These examples use the [httpie](https://httpie.io) CLI to send HTTP requests. | ||
Call the application that will use a @Tool-annotated method to retrieve the context to answer your question. | ||
|
||
```shell | ||
http :8080/chat/method authorName=="J.R.R. Tolkien" -b | ||
``` | ||
|
||
Call the application that will use an MCP Server to retrieve the context to answer your question. | ||
|
||
```shell | ||
http :8080/chat/mcp question=="Does Spring AI supports a Modular RAG architecture? Please provide some references." | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
plugins { | ||
id 'java' | ||
id 'org.springframework.boot' | ||
id 'io.spring.dependency-management' | ||
id 'org.graalvm.buildtools.native' | ||
} | ||
|
||
group = 'com.thomasvitale' | ||
version = '0.0.1-SNAPSHOT' | ||
|
||
java { | ||
toolchain { | ||
languageVersion = JavaLanguageVersion.of(23) | ||
} | ||
} | ||
|
||
repositories { | ||
mavenCentral() | ||
maven { url 'https://repo.spring.io/milestone' } | ||
maven { url 'https://repo.spring.io/snapshot' } | ||
} | ||
|
||
dependencies { | ||
implementation platform("org.springframework.ai:spring-ai-bom:${springAiVersion}") | ||
|
||
implementation 'org.springframework.boot:spring-boot-starter-web' | ||
implementation "org.springframework.ai:spring-ai-ollama-spring-boot-starter" | ||
|
||
implementation "org.springframework.experimental:spring-ai-mcp:${springAiMcpVersion}" | ||
|
||
testAndDevelopmentOnly 'org.springframework.boot:spring-boot-devtools' | ||
|
||
testImplementation 'org.springframework.boot:spring-boot-starter-test' | ||
testImplementation 'org.springframework.ai:spring-ai-spring-boot-testcontainers' | ||
testImplementation 'org.testcontainers:ollama' | ||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher' | ||
} | ||
|
||
tasks.named('test') { | ||
useJUnitPlatform() | ||
} |
31 changes: 31 additions & 0 deletions
31
labs/tools/src/main/java/com/thomasvitale/ai/spring/BookService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package com.thomasvitale.ai.spring; | ||
|
||
import org.springframework.stereotype.Service; | ||
|
||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
|
||
@Service | ||
public class BookService { | ||
|
||
private static final Map<Integer,Book> books = new ConcurrentHashMap<>(); | ||
|
||
static { | ||
books.put(1, new Book("His Dark Materials", "Philip Pullman")); | ||
books.put(2, new Book("Narnia", "C.S. Lewis")); | ||
books.put(3, new Book("The Hobbit", "J.R.R. Tolkien")); | ||
books.put(4, new Book("The Lord of The Rings", "J.R.R. Tolkien")); | ||
books.put(5, new Book("The Silmarillion", "J.R.R. Tolkien")); | ||
} | ||
|
||
public List<Book> getBooksByAuthor(Author author) { | ||
return books.values().stream() | ||
.filter(book -> author.name().equals(book.author())) | ||
.toList(); | ||
} | ||
|
||
public record Book(String title, String author) {} | ||
public record Author(String name) {} | ||
|
||
} |
54 changes: 54 additions & 0 deletions
54
labs/tools/src/main/java/com/thomasvitale/ai/spring/ChatController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package com.thomasvitale.ai.spring; | ||
|
||
import com.thomasvitale.ai.spring.api.tools.mcp.McpToolCallbackResolver; | ||
import com.thomasvitale.ai.spring.api.tools.method.MethodToolCallbackResolver; | ||
import org.springframework.ai.chat.client.ChatClient; | ||
import org.springframework.ai.mcp.client.McpSyncClient; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
/** | ||
* Chat examples using the high-level ChatClient API. | ||
*/ | ||
@RestController | ||
class ChatController { | ||
|
||
private final ChatClient chatClient; | ||
private final McpSyncClient mcpClient; | ||
private final Tools tools; | ||
|
||
ChatController(ChatClient.Builder chatClientBuilder, McpSyncClient mcpClient, Tools tools) { | ||
this.chatClient = chatClientBuilder.build(); | ||
this.mcpClient = mcpClient; | ||
this.tools = tools; | ||
} | ||
|
||
@GetMapping("/chat/method") | ||
String chatMethod(String authorName) { | ||
var userPromptTemplate = "What books written by {author} are available in the library?"; | ||
return chatClient.prompt() | ||
.user(userSpec -> userSpec | ||
.text(userPromptTemplate) | ||
.param("author", authorName) | ||
) | ||
.functions(MethodToolCallbackResolver.builder() | ||
.target(tools) | ||
.build() | ||
.getToolCallbacks()) | ||
.call() | ||
.content(); | ||
} | ||
|
||
@GetMapping("/chat/mcp") | ||
String chatMcp(String question) { | ||
return chatClient.prompt() | ||
.user(question) | ||
.functions(McpToolCallbackResolver.builder() | ||
.mcpClients(mcpClient) | ||
.build() | ||
.getToolCallbacks()) | ||
.call() | ||
.content(); | ||
} | ||
|
||
} |
19 changes: 19 additions & 0 deletions
19
labs/tools/src/main/java/com/thomasvitale/ai/spring/Functions.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package com.thomasvitale.ai.spring; | ||
|
||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.context.annotation.Description; | ||
|
||
import java.util.List; | ||
import java.util.function.Function; | ||
|
||
@Configuration(proxyBeanMethods = false) | ||
class Functions { | ||
|
||
@Bean | ||
@Description("Get the list of books written by the given author available in the library") | ||
Function<BookService.Author, List<BookService.Book>> booksByAuthor(BookService bookService) { | ||
return bookService::getBooksByAuthor; | ||
} | ||
|
||
} |
37 changes: 37 additions & 0 deletions
37
labs/tools/src/main/java/com/thomasvitale/ai/spring/LabsToolsApplication.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package com.thomasvitale.ai.spring; | ||
|
||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.springframework.ai.mcp.client.McpClient; | ||
import org.springframework.ai.mcp.client.McpSyncClient; | ||
import org.springframework.ai.mcp.client.stdio.ServerParameters; | ||
import org.springframework.ai.mcp.client.stdio.StdioClientTransport; | ||
import org.springframework.boot.SpringApplication; | ||
import org.springframework.boot.autoconfigure.SpringBootApplication; | ||
import org.springframework.context.annotation.Bean; | ||
|
||
@SpringBootApplication | ||
public class LabsToolsApplication { | ||
|
||
private static final Logger logger = LoggerFactory.getLogger(LabsToolsApplication.class); | ||
|
||
public static void main(String[] args) { | ||
SpringApplication.run(LabsToolsApplication.class, args); | ||
} | ||
|
||
@Bean | ||
McpSyncClient mcpClient() { | ||
var serverParameters = ServerParameters.builder("npx") | ||
.args("-y", "@modelcontextprotocol/server-brave-search") | ||
.addEnvVar("BRAVE_API_KEY", System.getenv("BRAVE_API_KEY")) | ||
.build(); | ||
|
||
var mcpClient = McpClient.using(new StdioClientTransport(serverParameters)).sync(); | ||
|
||
var initializeResult = mcpClient.initialize(); | ||
logger.info("MCP Initialized: {}", initializeResult); | ||
|
||
return mcpClient; | ||
} | ||
|
||
} |
22 changes: 22 additions & 0 deletions
22
labs/tools/src/main/java/com/thomasvitale/ai/spring/Tools.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package com.thomasvitale.ai.spring; | ||
|
||
import com.thomasvitale.ai.spring.api.tools.Tool; | ||
import org.springframework.stereotype.Component; | ||
|
||
import java.util.List; | ||
|
||
@Component | ||
public class Tools { | ||
|
||
private final BookService bookService; | ||
|
||
Tools(BookService bookService) { | ||
this.bookService = bookService; | ||
} | ||
|
||
@Tool("Get the list of books written by the given author available in the library") | ||
public List<BookService.Book> booksByAuthor(String author) { | ||
return bookService.getBooksByAuthor(new BookService.Author(author)); | ||
} | ||
|
||
} |
16 changes: 16 additions & 0 deletions
16
labs/tools/src/main/java/com/thomasvitale/ai/spring/api/tools/Tool.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package com.thomasvitale.ai.spring.api.tools; | ||
|
||
import java.lang.annotation.Documented; | ||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
|
||
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
@Documented | ||
public @interface Tool { | ||
|
||
String value() default ""; | ||
|
||
} |
6 changes: 6 additions & 0 deletions
6
labs/tools/src/main/java/com/thomasvitale/ai/spring/api/tools/ToolCallback.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package com.thomasvitale.ai.spring.api.tools; | ||
|
||
import org.springframework.ai.model.function.FunctionCallback; | ||
|
||
public interface ToolCallback extends FunctionCallback { | ||
} |
9 changes: 9 additions & 0 deletions
9
labs/tools/src/main/java/com/thomasvitale/ai/spring/api/tools/ToolCallbackResolver.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.thomasvitale.ai.spring.api.tools; | ||
|
||
import org.springframework.ai.model.function.FunctionCallback; | ||
|
||
public interface ToolCallbackResolver { | ||
|
||
FunctionCallback[] getToolCallbacks(); | ||
|
||
} |
14 changes: 14 additions & 0 deletions
14
labs/tools/src/main/java/com/thomasvitale/ai/spring/api/tools/mcp/McpToolCallback.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package com.thomasvitale.ai.spring.api.tools.mcp; | ||
|
||
import com.thomasvitale.ai.spring.api.tools.ToolCallback; | ||
import org.springframework.ai.mcp.client.McpSyncClient; | ||
import org.springframework.ai.mcp.spec.McpSchema; | ||
import org.springframework.ai.mcp.spring.McpFunctionCallback; | ||
|
||
public class McpToolCallback extends McpFunctionCallback implements ToolCallback { | ||
|
||
public McpToolCallback(McpSyncClient clientSession, McpSchema.Tool tool) { | ||
super(clientSession, tool); | ||
} | ||
|
||
} |
Oops, something went wrong.