mirror of
https://github.com/cline/cline.git
synced 2026-09-17 17:45:33 +08:00
Add gRPC recorder request filtering logic to make it more generic and configurable (#6070)
Add gRPC recorder request filtering logic to make it more generic and configurable #6070
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"claude-dev": patch
|
||||
---
|
||||
|
||||
Add gRPC recorder request filtering logic to make it more generic and configurable
|
||||
+13
-13
@@ -1,14 +1,14 @@
|
||||
// HubSpot Tracking Code for Cline Documentation
|
||||
(function() {
|
||||
// Check if HubSpot script is already loaded to prevent duplicates
|
||||
if (!document.getElementById('hs-script-loader')) {
|
||||
var script = document.createElement('script');
|
||||
script.type = 'text/javascript';
|
||||
script.id = 'hs-script-loader';
|
||||
script.async = true;
|
||||
script.src = 'https://js-na2.hs-scripts.com/243656267.js';
|
||||
|
||||
// Append the script to the document head
|
||||
document.head.appendChild(script);
|
||||
}
|
||||
})();
|
||||
;(() => {
|
||||
// Check if HubSpot script is already loaded to prevent duplicates
|
||||
if (!document.getElementById("hs-script-loader")) {
|
||||
var script = document.createElement("script")
|
||||
script.type = "text/javascript"
|
||||
script.id = "hs-script-loader"
|
||||
script.async = true
|
||||
script.src = "https://js-na2.hs-scripts.com/243656267.js"
|
||||
|
||||
// Append the script to the document head
|
||||
document.head.appendChild(script)
|
||||
}
|
||||
})()
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { GrpcRequestFilter } from "@core/controller/grpc-recorder/types"
|
||||
import { GrpcRecorder, GrpcRecorderNoops, IRecorder } from "@/core/controller/grpc-recorder/grpc-recorder"
|
||||
import { LogFileHandler, LogFileHandlerNoops } from "@/core/controller/grpc-recorder/log-file-handler"
|
||||
|
||||
@@ -11,6 +12,7 @@ import { LogFileHandler, LogFileHandlerNoops } from "@/core/controller/grpc-reco
|
||||
export class GrpcRecorderBuilder {
|
||||
private fileHandler: LogFileHandler | null = null
|
||||
private enabled: boolean = true
|
||||
private filters: GrpcRequestFilter[] = []
|
||||
|
||||
public withLogFileHandler(handler: LogFileHandler): this {
|
||||
this.fileHandler = handler
|
||||
@@ -22,12 +24,40 @@ export class GrpcRecorderBuilder {
|
||||
return this
|
||||
}
|
||||
|
||||
public withFilters(...filters: GrpcRequestFilter[]): this {
|
||||
this.filters.push(...filters)
|
||||
return this
|
||||
}
|
||||
|
||||
public build(): IRecorder {
|
||||
if (!this.enabled) {
|
||||
return new GrpcRecorderNoops()
|
||||
}
|
||||
|
||||
let filters: GrpcRequestFilter[] = filtersFromEnv()
|
||||
if (this.filters.length > 0) {
|
||||
filters = filters.concat(this.filters)
|
||||
}
|
||||
|
||||
const handler = this.fileHandler ?? new LogFileHandlerNoops()
|
||||
return new GrpcRecorder(handler)
|
||||
return new GrpcRecorder(handler, filters)
|
||||
}
|
||||
}
|
||||
|
||||
function filtersFromEnv(): GrpcRequestFilter[] {
|
||||
const filters: GrpcRequestFilter[] = []
|
||||
|
||||
if (process.env.GRPC_RECORDER_TESTS_FILTERS_ENABLED === "true") {
|
||||
filters.push(...testFilters())
|
||||
}
|
||||
|
||||
return filters
|
||||
}
|
||||
|
||||
function testFilters(): GrpcRequestFilter[] {
|
||||
/*
|
||||
* Ignores streaming messages and unwanted services messages
|
||||
* that record more than expected.
|
||||
*/
|
||||
return [(req) => req.is_streaming, (req) => ["cline.UiService", "cline.McpService", "cline.WebService"].includes(req.service)]
|
||||
}
|
||||
|
||||
@@ -7,7 +7,10 @@ describe("grpc-recorder", () => {
|
||||
let recorder: IRecorder
|
||||
|
||||
before(async () => {
|
||||
recorder = GrpcRecorder.builder().enableIf(true).build()
|
||||
recorder = GrpcRecorder.builder()
|
||||
.withFilters((req: GrpcRequest) => req.service === "the-unwanted-service")
|
||||
.enableIf(true)
|
||||
.build()
|
||||
})
|
||||
|
||||
describe("GrpcRecorder", () => {
|
||||
@@ -73,7 +76,7 @@ describe("grpc-recorder", () => {
|
||||
recorder.recordRequest(us.request)
|
||||
|
||||
let sessionLog = recorder.getSessionLog()
|
||||
expect(sessionLog.entries).length(index + 1)
|
||||
expect(sessionLog.entries).length(index + 1, `unexpected request_id: ${us.request.request_id}`)
|
||||
|
||||
expect(sessionLog.entries[index]).to.include({
|
||||
service: us.request.service,
|
||||
@@ -101,6 +104,48 @@ describe("grpc-recorder", () => {
|
||||
completedRequests: 2,
|
||||
errorRequests: 1,
|
||||
})
|
||||
|
||||
recorder.recordRequest({
|
||||
service: "the-unwanted-service",
|
||||
method: "the-method",
|
||||
message: "the-message",
|
||||
request_id: "request-id-1",
|
||||
is_streaming: false,
|
||||
})
|
||||
|
||||
// we expect to filter out this unwanted request
|
||||
expect(sessionLog.entries).length(3)
|
||||
})
|
||||
|
||||
it("using default filtering should filter out unwanted requests", async () => {
|
||||
const customRecorder = GrpcRecorder.builder()
|
||||
.withFilters(
|
||||
(req) => req.is_streaming,
|
||||
(req) => ["cline.UiService", "cline.McpService", "cline.WebService"].includes(req.service),
|
||||
)
|
||||
.enableIf(true)
|
||||
.build()
|
||||
const unwantedServices = ["cline.UiService", "cline.McpService", "cline.WebService"]
|
||||
unwantedServices.forEach((us) => {
|
||||
customRecorder.recordRequest({
|
||||
service: us,
|
||||
method: "the-method",
|
||||
message: "the-message",
|
||||
request_id: "request-id-1",
|
||||
is_streaming: false,
|
||||
})
|
||||
})
|
||||
let sessionLog = customRecorder.getSessionLog()
|
||||
expect(sessionLog.entries).length(0)
|
||||
customRecorder.recordRequest({
|
||||
service: "streaming-request",
|
||||
method: "the-method",
|
||||
message: "the-message",
|
||||
request_id: "request-id-1",
|
||||
is_streaming: true,
|
||||
})
|
||||
sessionLog = customRecorder.getSessionLog()
|
||||
expect(sessionLog.entries).length(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -2,7 +2,7 @@ import { GrpcResponse } from "@shared/ExtensionMessage"
|
||||
import { GrpcRequest } from "@shared/WebviewMessage"
|
||||
import { GrpcRecorderBuilder } from "@/core/controller/grpc-recorder/grpc-recorder.builder"
|
||||
import { ILogFileHandler } from "@/core/controller/grpc-recorder/log-file-handler"
|
||||
import { GrpcLogEntry, GrpcSessionLog, SessionStats } from "@/core/controller/grpc-recorder/types"
|
||||
import { GrpcLogEntry, GrpcRequestFilter, GrpcSessionLog, SessionStats } from "@/core/controller/grpc-recorder/types"
|
||||
|
||||
export class GrpcRecorderNoops implements IRecorder {
|
||||
recordRequest(_request: GrpcRequest): void {}
|
||||
@@ -36,7 +36,10 @@ export class GrpcRecorder implements IRecorder {
|
||||
private sessionLog: GrpcSessionLog
|
||||
private pendingRequests: Map<string, { entry: GrpcLogEntry; startTime: number }> = new Map()
|
||||
|
||||
constructor(private fileHandler: ILogFileHandler) {
|
||||
constructor(
|
||||
private fileHandler: ILogFileHandler,
|
||||
private requestFilters: GrpcRequestFilter[] = [],
|
||||
) {
|
||||
this.sessionLog = {
|
||||
startTime: new Date().toISOString(),
|
||||
entries: [],
|
||||
@@ -61,6 +64,10 @@ export class GrpcRecorder implements IRecorder {
|
||||
* @param request - The incoming gRPC request.
|
||||
*/
|
||||
public recordRequest(request: GrpcRequest): void {
|
||||
if (this.shouldFilter(request)) {
|
||||
return
|
||||
}
|
||||
|
||||
const entry: GrpcLogEntry = {
|
||||
requestId: request.request_id,
|
||||
service: request.service,
|
||||
@@ -176,4 +183,8 @@ export class GrpcRecorder implements IRecorder {
|
||||
errorRequests,
|
||||
}
|
||||
}
|
||||
|
||||
private shouldFilter(request: GrpcRequest): boolean {
|
||||
return this.requestFilters.some((filter) => filter(request))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import { GrpcRequest } from "@/shared/WebviewMessage"
|
||||
|
||||
export type GrpcRequestFilter = (request: GrpcRequest) => boolean
|
||||
|
||||
export interface GrpcLogEntry {
|
||||
requestId: string
|
||||
service: string
|
||||
|
||||
@@ -238,6 +238,7 @@ export const e2e = test
|
||||
CLINE_ENVIRONMENT: "local",
|
||||
GRPC_RECORDER_FILE_NAME: E2ETestHelper.generateTestFileName(testInfo.title, testInfo.project.name),
|
||||
// GRPC_RECORDER_ENABLED: "true",
|
||||
// GRPC_RECORDER_TESTS_FILTERS_ENABLED: "true"
|
||||
// IS_DEV: "true",
|
||||
// DEV_WORKSPACE_FOLDER: E2ETestHelper.CODEBASE_ROOT_DIR,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user