Merge pull request #97 from huerlisi/feat/paginate-job-logs
feat: add pagination support for CI job logs to prevent context window flooding
This commit is contained in:
40
index.ts
40
index.ts
@ -523,7 +523,7 @@ const allTools = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "get_pipeline_job_output",
|
name: "get_pipeline_job_output",
|
||||||
description: "Get the output/trace of a GitLab pipeline job number",
|
description: "Get the output/trace of a GitLab pipeline job with optional pagination to limit context window usage",
|
||||||
inputSchema: zodToJsonSchema(GetPipelineJobOutputSchema),
|
inputSchema: zodToJsonSchema(GetPipelineJobOutputSchema),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -2611,9 +2611,11 @@ async function getPipelineJob(projectId: string, jobId: number): Promise<GitLabP
|
|||||||
*
|
*
|
||||||
* @param {string} projectId - The ID or URL-encoded path of the project
|
* @param {string} projectId - The ID or URL-encoded path of the project
|
||||||
* @param {number} jobId - The ID of the job
|
* @param {number} jobId - The ID of the job
|
||||||
|
* @param {number} limit - Maximum number of lines to return from the end (default: 1000)
|
||||||
|
* @param {number} offset - Number of lines to skip from the end (default: 0)
|
||||||
* @returns {Promise<string>} The job output/trace
|
* @returns {Promise<string>} The job output/trace
|
||||||
*/
|
*/
|
||||||
async function getPipelineJobOutput(projectId: string, jobId: number): Promise<string> {
|
async function getPipelineJobOutput(projectId: string, jobId: number, limit?: number, offset?: number): Promise<string> {
|
||||||
projectId = decodeURIComponent(projectId); // Decode project ID
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
||||||
const url = new URL(
|
const url = new URL(
|
||||||
`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/jobs/${jobId}/trace`
|
`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/jobs/${jobId}/trace`
|
||||||
@ -2632,7 +2634,35 @@ async function getPipelineJobOutput(projectId: string, jobId: number): Promise<s
|
|||||||
}
|
}
|
||||||
|
|
||||||
await handleGitLabError(response);
|
await handleGitLabError(response);
|
||||||
return await response.text();
|
const fullTrace = await response.text();
|
||||||
|
|
||||||
|
// Apply client-side pagination to limit context window usage
|
||||||
|
if (limit !== undefined || offset !== undefined) {
|
||||||
|
const lines = fullTrace.split('\n');
|
||||||
|
const startOffset = offset || 0;
|
||||||
|
const maxLines = limit || 1000;
|
||||||
|
|
||||||
|
// Return lines from the end, skipping offset lines and limiting to maxLines
|
||||||
|
const startIndex = Math.max(0, lines.length - startOffset - maxLines);
|
||||||
|
const endIndex = lines.length - startOffset;
|
||||||
|
|
||||||
|
const selectedLines = lines.slice(startIndex, endIndex);
|
||||||
|
const result = selectedLines.join('\n');
|
||||||
|
|
||||||
|
// Add metadata about truncation
|
||||||
|
if (startIndex > 0 || endIndex < lines.length) {
|
||||||
|
const totalLines = lines.length;
|
||||||
|
const shownLines = selectedLines.length;
|
||||||
|
const skippedFromStart = startIndex;
|
||||||
|
const skippedFromEnd = startOffset;
|
||||||
|
|
||||||
|
return `[Log truncated: showing ${shownLines} of ${totalLines} lines, skipped ${skippedFromStart} from start, ${skippedFromEnd} from end]\n\n${result}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fullTrace;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -3732,8 +3762,8 @@ server.setRequestHandler(CallToolRequestSchema, async request => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case "get_pipeline_job_output": {
|
case "get_pipeline_job_output": {
|
||||||
const { project_id, job_id } = GetPipelineJobOutputSchema.parse(request.params.arguments);
|
const { project_id, job_id, limit, offset } = GetPipelineJobOutputSchema.parse(request.params.arguments);
|
||||||
const jobOutput = await getPipelineJobOutput(project_id, job_id);
|
const jobOutput = await getPipelineJobOutput(project_id, job_id, limit, offset);
|
||||||
return {
|
return {
|
||||||
content: [
|
content: [
|
||||||
{
|
{
|
||||||
|
@ -191,6 +191,8 @@ export const CancelPipelineSchema = z.object({
|
|||||||
export const GetPipelineJobOutputSchema = z.object({
|
export const GetPipelineJobOutputSchema = z.object({
|
||||||
project_id: z.string().describe("Project ID or URL-encoded path"),
|
project_id: z.string().describe("Project ID or URL-encoded path"),
|
||||||
job_id: z.number().describe("The ID of the job"),
|
job_id: z.number().describe("The ID of the job"),
|
||||||
|
limit: z.number().optional().describe("Maximum number of lines to return from the end of the log (default: 1000)"),
|
||||||
|
offset: z.number().optional().describe("Number of lines to skip from the end of the log (default: 0)"),
|
||||||
});
|
});
|
||||||
|
|
||||||
// User schemas
|
// User schemas
|
||||||
|
Reference in New Issue
Block a user