|
3 | 3 | import org.slf4j.Logger; |
4 | 4 | import org.slf4j.LoggerFactory; |
5 | 5 | import org.springframework.ai.chat.client.ChatClient; |
6 | | -import org.springframework.beans.factory.annotation.Value; |
7 | 6 | import org.springframework.stereotype.Service; |
8 | 7 |
|
9 | 8 | /** |
10 | 9 | * Spring AI + Amazon Bedrock Converse. Builds one {@link ChatClient} |
11 | | - * configured with the system prompt and the always-registered |
12 | | - * {@link PyroscopeTool}, then per-analysis layers in a fresh |
13 | | - * {@link GitHubSourceCodeTool} when the target workload advertises a |
14 | | - * GitHub repo via pod annotation or task tag. |
| 10 | + * configured with the system prompt and the two tools the model calls: |
| 11 | + * {@link PyroscopeTool} and {@link GitHubSourceCodeTool}. |
15 | 12 | * |
16 | | - * Per-analysis tool registration lets the analyzer serve many workloads |
17 | | - * whose sources live in different repositories without any environment |
18 | | - * configuration on the analyzer side. |
| 13 | + * Both tools are singletons. Per-analysis context (workload service name, |
| 14 | + * time window, repo coordinates) is communicated to the model through the |
| 15 | + * user prompt — the model passes those values back when it calls a tool. |
19 | 16 | */ |
20 | 17 | @Service |
21 | 18 | public class AiService { |
@@ -50,44 +47,33 @@ public class AiService { |
50 | 47 | """; |
51 | 48 |
|
52 | 49 | private final ChatClient chatClient; |
53 | | - private final String githubToken; |
54 | 50 |
|
55 | 51 | public AiService( |
56 | 52 | ChatClient.Builder chatClientBuilder, |
57 | 53 | PyroscopeTool pyroscopeTool, |
58 | | - @Value("${GITHUB_TOKEN:}") String githubToken |
| 54 | + GitHubSourceCodeTool githubSourceCodeTool |
59 | 55 | ) { |
60 | | - this.githubToken = githubToken; |
61 | 56 | this.chatClient = chatClientBuilder |
62 | 57 | .defaultSystem(SYSTEM_PROMPT) |
63 | | - .defaultTools(pyroscopeTool) |
| 58 | + .defaultTools(pyroscopeTool, githubSourceCodeTool) |
64 | 59 | .build(); |
65 | 60 | } |
66 | 61 |
|
67 | 62 | /** Runs the analysis and returns the Markdown report content. */ |
68 | 63 | public String analyze(AnalysisService.AnalysisContext ctx) { |
69 | | - var sourceCodeTool = buildSourceCodeTool(ctx); |
70 | | - var prompt = buildPrompt(ctx, sourceCodeTool != null); |
| 64 | + var hasRepo = ctx.githubRepo() != null && !ctx.githubRepo().isBlank(); |
| 65 | + var prompt = buildPrompt(ctx, hasRepo); |
71 | 66 |
|
72 | | - logger.info("Sending analysis request to Amazon Bedrock: analysisId={} service={} sourceTool={}", |
73 | | - ctx.analysisId(), ctx.request().service(), sourceCodeTool != null); |
| 67 | + logger.info("Sending analysis request to Amazon Bedrock: analysisId={} service={} repo={}", |
| 68 | + ctx.analysisId(), ctx.request().service(), hasRepo ? ctx.githubRepo() : "(none)"); |
74 | 69 |
|
75 | | - var spec = chatClient.prompt().user(prompt); |
76 | | - if (sourceCodeTool != null) { |
77 | | - spec = spec.tools(sourceCodeTool); |
78 | | - } |
79 | | - var response = spec.call().content(); |
| 70 | + var response = chatClient.prompt().user(prompt).call().content(); |
80 | 71 |
|
81 | 72 | logger.info("Received analysis response from Amazon Bedrock: analysisId={} length={}", |
82 | 73 | ctx.analysisId(), response == null ? 0 : response.length()); |
83 | 74 | return response == null ? "# Analysis\n\n_Model returned no content._\n" : response; |
84 | 75 | } |
85 | 76 |
|
86 | | - private GitHubSourceCodeTool buildSourceCodeTool(AnalysisService.AnalysisContext ctx) { |
87 | | - if (ctx.githubRepo() == null || ctx.githubRepo().isBlank()) return null; |
88 | | - return new GitHubSourceCodeTool(ctx.githubRepo(), ctx.githubPath(), githubToken); |
89 | | - } |
90 | | - |
91 | 77 | String buildPrompt(AnalysisService.AnalysisContext ctx, boolean sourceCodeAvailable) { |
92 | 78 | var r = ctx.request(); |
93 | 79 | var sb = new StringBuilder(); |
@@ -152,27 +138,36 @@ blocking waits and contention (wall view). Flag resource pressure, |
152 | 138 | Be concise. |
153 | 139 | """); |
154 | 140 |
|
| 141 | + sb.append(""" |
| 142 | +
|
| 143 | + --- |
| 144 | +
|
| 145 | + You have a Pyroscope query tool you can invoke to request |
| 146 | + additional time windows or narrower label selectors if you need |
| 147 | + to confirm a hypothesis before writing the report. |
| 148 | + """); |
| 149 | + |
155 | 150 | if (sourceCodeAvailable) { |
156 | 151 | sb.append(""" |
157 | 152 |
|
158 | | - --- |
| 153 | + You also have a GitHub source-code tool. Use it to look up |
| 154 | + the actual source of methods that appear in Pyroscope top |
| 155 | + functions, JFR events, or the thread dump. The target |
| 156 | + workload's source lives at: |
159 | 157 |
|
160 | | - You have a source code tool. Use it to look up the actual |
161 | | - source of methods that appear in Pyroscope top functions, |
162 | | - JFR events, or the thread dump. In your findings and |
163 | | - recommendations, reference specific file paths, class names |
164 | | - and line numbers. Provide concrete code fixes — show the |
165 | | - current problematic code and the recommended replacement. |
| 158 | + """); |
| 159 | + sb.append("- repo: `").append(ctx.githubRepo()).append("`\n"); |
| 160 | + var path = ctx.githubPath() == null ? "" : ctx.githubPath(); |
| 161 | + sb.append("- pathPrefix: `").append(path).append("`\n\n"); |
| 162 | + sb.append(""" |
| 163 | + Pass those values along with the file path on every call. |
| 164 | + In your findings and recommendations, reference specific |
| 165 | + file paths, class names and line numbers. Provide concrete |
| 166 | + code fixes — show the current problematic code and the |
| 167 | + recommended replacement. |
166 | 168 | """); |
167 | 169 | } |
168 | 170 |
|
169 | | - sb.append(""" |
170 | | -
|
171 | | - You also have a Pyroscope query tool you can invoke to request |
172 | | - additional time windows or narrower label selectors if you need |
173 | | - to confirm a hypothesis before writing the report. |
174 | | - """); |
175 | | - |
176 | 171 | return sb.toString(); |
177 | 172 | } |
178 | 173 |
|
|
0 commit comments