Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pull request review cache #510

Merged
merged 4 commits into from
Jun 5, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
1 change: 1 addition & 0 deletions docs/src/content/docs/reference/cli/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Options:
-prc, --pull-request-comment [string] create comment on a pull request.
-prd, --pull-request-description [string] upsert comment on a pull request description.
pelikhan marked this conversation as resolved.
Show resolved Hide resolved
pelikhan marked this conversation as resolved.
Show resolved Hide resolved
-prr, --pull-request-reviews create pull request reviews from annotations
--no-pull-request-reviews-cache disable pull request reviews cache
-j, --json emit full JSON response to output
-y, --yaml emit full YAML response to output
-p, --prompt dry run, don't execute LLM and return expanded prompt
Expand Down
4 changes: 4 additions & 0 deletions packages/cli/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ export async function cli() {
"-prr, --pull-request-reviews",
"create pull request reviews from annotations"
)
.option(
"--no-pull-request-reviews-cache",
"disable pull request reviews cache"
)
.option("-j, --json", "emit full JSON response to output")
.option("-y, --yaml", "emit full YAML response to output")
.option(
Expand Down
22 changes: 20 additions & 2 deletions packages/cli/src/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ import {
githubUpsetPullRequest,
githubCreatePullRequestReviews,
githubCreateIssueComment,
GITHUB_PULL_REQUEST_REVIEWS_CACHE,
JSONLineCache,
PullRequestReviewsCacheKey,
PullRequestReviewsCacheValue,
} from "genaiscript-core"
import { capitalize } from "inflection"
import { basename, resolve, join, relative } from "node:path"
Expand Down Expand Up @@ -62,6 +66,7 @@ export async function runScript(
pullRequestComment: string | boolean
pullRequestDescription: string | boolean
pullRequestReviews: boolean
pullRequestReviewsCache: boolean
outData: string
label: string
temperature: string
Expand Down Expand Up @@ -101,6 +106,7 @@ export async function runScript(
const maxTokens = normalizeInt(options.maxTokens)
const maxToolCalls = normalizeInt(options.maxToolCalls)
const cache = !!options.cache
const pullRequestReviewsCache = !!options.pullRequestReviewsCache
const applyEdits = !!options.applyEdits
const csvSeparator = options.csvSeparator || "\t"
const removeOut = options.removeOut
Expand Down Expand Up @@ -368,8 +374,20 @@ ${Array.from(files)

if (pullRequestReviews && res.annotations?.length) {
const info = parseGHTokenFromEnv(process.env)
if (info.repository && info.issue)
await githubCreatePullRequestReviews(script, info, res.annotations)
if (info.repository && info.issue) {
const cache = pullRequestReviewsCache
? JSONLineCache.byName<
PullRequestReviewsCacheKey,
PullRequestReviewsCacheValue
>(GITHUB_PULL_REQUEST_REVIEWS_CACHE)
: undefined
await githubCreatePullRequestReviews(
script,
info,
res.annotations,
{ cache }
)
}
}

if (pullRequestComment && res.text) {
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ import { CORE_VERSION } from "./version"

export type CacheEntry<K, V> = { sha: string; key: K; val: V }

export class Cache<K, V> extends EventTarget {
export class JSONLineCache<K, V> extends EventTarget {
private _entries: Record<string, CacheEntry<K, V>>
private constructor(public readonly name: string) {
super()
}

static byName<K, V>(name: string): Cache<K, V> {
static byName<K, V>(name: string): JSONLineCache<K, V> {
name = name.replace(/[^a-z0-9_]/gi, "_")
const key = "cacheKV." + name
if (host.userState[key]) return host.userState[key]
const r = new Cache<K, V>(name)
const r = new JSONLineCache<K, V>(name)
host.userState[key] = r
return r
}
Expand Down
9 changes: 5 additions & 4 deletions packages/core/src/chat.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import OpenAI from "openai"
import { Cache } from "./cache"
import { JSONLineCache } from "./cache"
import { MarkdownTrace } from "./trace"
import { PromptImage } from "./promptdom"
import { AICIRequest } from "./aici"
Expand All @@ -16,6 +16,7 @@ import {
import { validateFencesWithSchema, validateJSONWithSchema } from "./schema"
import dedent from "ts-dedent"
import {
CHAT_CACHE,
DEFAULT_MODEL,
DEFAULT_TEMPERATURE,
MAX_DATA_REPAIRS,
Expand Down Expand Up @@ -87,18 +88,18 @@ export type ChatCompletationRequestCacheValue = {
finishReason: ChatCompletionResponse["finishReason"]
}

export type ChatCompletationRequestCache = Cache<
export type ChatCompletationRequestCache = JSONLineCache<
ChatCompletionRequestCacheKey,
ChatCompletationRequestCacheValue
>

export function getChatCompletionCache(
name?: string
): ChatCompletationRequestCache {
return Cache.byName<
return JSONLineCache.byName<
ChatCompletionRequestCacheKey,
ChatCompletationRequestCacheValue
>(name || "chatv2")
>(name || CHAT_CACHE)
}

export interface ChatCompletionsProgressReport {
Expand Down
6 changes: 5 additions & 1 deletion packages/core/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,4 +170,8 @@ export const DOCKER_CONTAINER_VOLUME = "/app"
export const CLI_RUN_FILES_FOLDER = "files"

export const GITHUB_API_VERSION = "2022-11-28"
export const GITHUB_TOKEN = "GITHUB_TOKEN"
export const GITHUB_TOKEN = "GITHUB_TOKEN"

export const AI_REQUESTS_CACHE = "airequests"
export const CHAT_CACHE = "chatv2"
export const GITHUB_PULL_REQUEST_REVIEWS_CACHE = "prr"
58 changes: 53 additions & 5 deletions packages/core/src/github.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import { assert } from "node:console"
import { GITHUB_API_VERSION, GITHUB_TOKEN } from "./constants"
import {
GITHUB_API_VERSION,
GITHUB_PULL_REQUEST_REVIEWS_CACHE,
GITHUB_TOKEN,
} from "./constants"
import { createFetch } from "./fetch"
import { host } from "./host"
import { link, prettifyMarkdown } from "./markdown"
import { logError, logVerbose, normalizeInt } from "./util"
import { string } from "mathjs"
import { JSONLineCache } from "./cache"

export interface GithubConnectionInfo {
token: string
Expand Down Expand Up @@ -211,6 +217,7 @@ async function githubCreatePullRequestReview(
) {
assert(token)
const { apiUrl, repository, issue, commitSha } = info

const fetch = await createFetch({ retryOn: [] })
const url = `${apiUrl}/repos/${repository}/pulls/${issue}/comments`
const body = {
Expand Down Expand Up @@ -244,13 +251,36 @@ async function githubCreatePullRequestReview(
return r
}

export interface PullRequestReviewsCacheKey {
repository: string
issue: number
scriptId: string
annotation: Diagnostic
}

export interface PullRequestReviewsCacheValue {
created: boolean
statusText: string
pelikhan marked this conversation as resolved.
Show resolved Hide resolved
pelikhan marked this conversation as resolved.
Show resolved Hide resolved
html_url: string
}
pelikhan marked this conversation as resolved.
Show resolved Hide resolved
pelikhan marked this conversation as resolved.
Show resolved Hide resolved
pelikhan marked this conversation as resolved.
Show resolved Hide resolved

export type PullRequestReviewsCache = JSONLineCache<
PullRequestReviewsCacheKey,
PullRequestReviewsCacheValue
>

export async function githubCreatePullRequestReviews(
script: PromptScript,
info: GithubConnectionInfo,
annotations: Diagnostic[]
annotations: Diagnostic[],
options?: {
cache?: PullRequestReviewsCache
}
): Promise<boolean> {
const { repository, issue, sha } = info
const { cache } = options || {}

if (!annotations?.length) return true
const { issue, sha } = info
if (!issue) {
logError("missing pull request number")
return false
Expand All @@ -266,7 +296,25 @@ export async function githubCreatePullRequestReviews(
}

// code annotations
for (const annotation of annotations)
await githubCreatePullRequestReview(script, info, token, annotation)
for (const annotation of annotations) {
const cacheKey = {
repository,
issue,
scriptId: script.id,
annotation,
}
const cached = await cache?.get(cacheKey)
if (cached)
logVerbose("ignore cached pull request review, " + cached.html_url)
else {
const res = await githubCreatePullRequestReview(
script,
info,
token,
annotation
)
if (res.created) await cache?.set(cacheKey, res)
}
}
return true
}
2 changes: 1 addition & 1 deletion packages/sample/genaisrc/summarize-link.genai.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ script({
temperature: 0,
tests: {
files: [
"https://raw.githubusercontent.com/microsoft/genaiscript/main/packages/sample/src/rag/markdown.md",
`https://raw.githubusercontent.com/microsoft/genaiscript/main/packages/sample/src/rag/markdown.md`,
],
keywords: "markdown",
},
Expand Down
4 changes: 2 additions & 2 deletions packages/vscode/src/airequesttree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
ExtensionState,
} from "./state"
import { CACHE_AIREQUEST_PREFIX, CHANGE, CacheEntry } from "genaiscript-core"
import { Cache } from "genaiscript-core"
import { JSONLineCache } from "genaiscript-core"
import { infoUri } from "./markdowndocumentprovider"
import { toMarkdownString } from "./markdown"

Expand All @@ -14,7 +14,7 @@ type AIRequestTreeNode = CacheEntry<AIRequestSnapshotKey, AIRequestSnapshot>
class AIRequestTreeDataProvider
implements vscode.TreeDataProvider<AIRequestTreeNode>
{
cache: Cache<AIRequestSnapshotKey, AIRequestSnapshot>
cache: JSONLineCache<AIRequestSnapshotKey, AIRequestSnapshot>
constructor(readonly state: ExtensionState) {
this.cache = state.aiRequestCache()
this.cache.addEventListener(CHANGE, () => this.refresh())
Expand Down
7 changes: 4 additions & 3 deletions packages/vscode/src/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
isCancelError,
delay,
CHANGE,
Cache,
JSONLineCache,
logInfo,
logMeasure,
parseAnnotations,
Expand All @@ -31,6 +31,7 @@ import {
fixPromptDefinitions,
DEFAULT_MODEL,
resolveModelConnectionInfo,
AI_REQUESTS_CACHE,
} from "genaiscript-core"
import { ExtensionContext } from "vscode"
import { VSCodeHost } from "./vshost"
Expand Down Expand Up @@ -124,7 +125,7 @@ export function snapshotAIRequest(r: AIRequest): AIRequestSnapshot {
}

function getAIRequestCache() {
return Cache.byName<AIRequestSnapshotKey, AIRequestSnapshot>("airequests")
return JSONLineCache.byName<AIRequestSnapshotKey, AIRequestSnapshot>(AI_REQUESTS_CACHE)
}

export class ExtensionState extends EventTarget {
Expand All @@ -133,7 +134,7 @@ export class ExtensionState extends EventTarget {
private _project: Project = undefined
private _aiRequest: AIRequest = undefined
private _diagColl: vscode.DiagnosticCollection
private _aiRequestCache: Cache<AIRequestSnapshotKey, AIRequestSnapshot> =
private _aiRequestCache: JSONLineCache<AIRequestSnapshotKey, AIRequestSnapshot> =
undefined
readonly output: vscode.LogOutputChannel

Expand Down
Loading