Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
16 changes: 14 additions & 2 deletions src/services/ghost/GhostProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { AutoTriggerStrategy } from "./strategies/AutoTriggerStrategy"
import { GhostModel } from "./GhostModel"
import { GhostWorkspaceEdit } from "./GhostWorkspaceEdit"
import { GhostDecorations } from "./GhostDecorations"
import { GhostSuggestionContext } from "./types"
import { GhostSuggestionContext, contextToAutocompleteInput, extractPrefixSuffix } from "./types"
import { GhostStatusBar } from "./GhostStatusBar"
import { GhostSuggestionsState } from "./GhostSuggestions"
import { GhostCodeActionProvider } from "./GhostCodeActionProvider"
Expand Down Expand Up @@ -267,7 +267,19 @@ export class GhostProvider {
this.isRequestCancelled = false

const context = await this.ghostContext.generate(initialContext)
const { systemPrompt, userPrompt } = this.autoTriggerStrategy.getPrompts(context)

const autocompleteInput = contextToAutocompleteInput(context)

const position = context.range?.start ?? context.document.positionAt(0)
const { prefix, suffix } = extractPrefixSuffix(context.document, position)
const languageId = context.document.languageId

const { systemPrompt, userPrompt } = this.autoTriggerStrategy.getPrompts(
autocompleteInput,
prefix,
suffix,
languageId,
)
if (this.isRequestCancelled) {
return
}
Expand Down
17 changes: 10 additions & 7 deletions src/services/ghost/__tests__/GhostModelPerformance.spec.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import * as vscode from "vscode"
import { describe, it, expect } from "vitest"
import { AutoTriggerStrategy } from "../strategies/AutoTriggerStrategy"
import { MockWorkspace } from "./MockWorkspace"
import { ApiHandler, buildApiHandler } from "../../../api"
import { GhostModel } from "../GhostModel"
import { allowNetConnect } from "../../../vitest.setup"
import { AutocompleteInput } from "../types"
import crypto from "crypto"

const KEYS = {
KILOCODE: null,
Expand All @@ -15,17 +16,19 @@ const KEYS = {
describe("GhostModelPerformance", () => {
const generatePrompt = (userInput: string) => {
const autoTriggerStrategy = new AutoTriggerStrategy()
const mockWorkspace = new MockWorkspace()

const testUri = vscode.Uri.parse("file:///example.ts")
const document = mockWorkspace.addDocument(testUri, "")

const context = {
userInput,
document: document,
const autocompleteInput: AutocompleteInput = {
isUntitledFile: false,
completionId: crypto.randomUUID(),
filepath: testUri.fsPath,
pos: { line: 0, character: 0 },
recentlyVisitedRanges: [],
recentlyEditedRanges: [],
}

const { systemPrompt, userPrompt } = autoTriggerStrategy.getPrompts(context)
const { systemPrompt, userPrompt } = autoTriggerStrategy.getPrompts(autocompleteInput, "", "", "typescript")

return { systemPrompt, suggestionPrompt: userPrompt }
}
Expand Down
16 changes: 13 additions & 3 deletions src/services/ghost/__tests__/GhostRecentOperations.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as vscode from "vscode"
import { GhostContext } from "../GhostContext"
import { GhostDocumentStore } from "../GhostDocumentStore"
import { AutoTriggerStrategy } from "../strategies/AutoTriggerStrategy"
import { GhostSuggestionContext } from "../types"
import { GhostSuggestionContext, contextToAutocompleteInput } from "../types"
import { MockTextDocument } from "../../mocking/MockTextDocument"

// Mock vscode
Expand Down Expand Up @@ -115,8 +115,13 @@ describe("GhostRecentOperations", () => {
expect(enrichedContext.recentOperations).toBeDefined()
expect(enrichedContext.recentOperations?.length).toBeGreaterThan(0)

const autocompleteInput = contextToAutocompleteInput(enrichedContext)

// Generate prompt
const { userPrompt } = autoTriggerStrategy.getPrompts(enrichedContext)
const prefix = enrichedContext.document.getText()
const suffix = ""
const languageId = enrichedContext.document.languageId
const { userPrompt } = autoTriggerStrategy.getPrompts(autocompleteInput, prefix, suffix, languageId)

// Verify that the prompt includes the recent operations section
// The new strategy system uses "## Recent Typing" format
Expand All @@ -132,8 +137,13 @@ describe("GhostRecentOperations", () => {
// Generate context
const enrichedContext = await context.generate(suggestionContext)

const autocompleteInput = contextToAutocompleteInput(enrichedContext)

// Generate prompt
const { userPrompt } = autoTriggerStrategy.getPrompts(enrichedContext)
const prefix = enrichedContext.document.getText()
const suffix = ""
const languageId = enrichedContext.document.languageId
const { userPrompt } = autoTriggerStrategy.getPrompts(autocompleteInput, prefix, suffix, languageId)

// Verify that the prompt does not include recent operations section
// The current document content will still be in the prompt, so we should only check
Expand Down
80 changes: 38 additions & 42 deletions src/services/ghost/strategies/AutoTriggerStrategy.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { GhostSuggestionContext, extractPrefix } from "../types"
import { AutocompleteInput } from "../types"
import { CURSOR_MARKER } from "../ghostConstants"
import { isCommentLine, extractComment, cleanComment } from "./CommentHelpers"
import { isCommentLine, cleanComment } from "./CommentHelpers"
import type { TextDocument, Range } from "vscode"

export function getBaseSystemInstructions(): string {
Expand Down Expand Up @@ -55,15 +55,6 @@ export function addCursorMarker(document: TextDocument, range?: Range): string {
return `${beforeCursor}${CURSOR_MARKER}${afterCursor}`
}

export function formatDocumentWithCursor(document: TextDocument, range?: Range, languageId?: string): string {
const lang = languageId || document.languageId
const codeWithCursor = addCursorMarker(document, range)

return `\`\`\`${lang}
${codeWithCursor}
\`\`\``
}

export class AutoTriggerStrategy {
shouldTreatAsComment(prefix: string, languageId: string): boolean {
const lines = prefix.split("\n")
Expand All @@ -79,22 +70,24 @@ export class AutoTriggerStrategy {
}
}

getPrompts(context: GhostSuggestionContext): {
getPrompts(
autocompleteInput: AutocompleteInput,
prefix: string,
suffix: string,
languageId: string,
): {
systemPrompt: string
userPrompt: string
} {
const prefix = extractPrefix(context)
const languageId = context.document?.languageId || ""

if (this.shouldTreatAsComment(prefix, languageId)) {
return {
systemPrompt: this.getCommentsSystemInstructions(),
userPrompt: this.getCommentsUserPrompt(context),
userPrompt: this.getCommentsUserPrompt(prefix, suffix, languageId),
}
} else {
return {
systemPrompt: this.getSystemInstructions(),
userPrompt: this.getUserPrompt(context),
userPrompt: this.getUserPrompt(autocompleteInput, prefix, suffix, languageId),
}
}
}
Expand All @@ -112,32 +105,29 @@ Provide non-intrusive completions after a typing pause. Be conservative and help
/**
* Build minimal prompt for auto-trigger
*/
getUserPrompt(context: GhostSuggestionContext): string {
getUserPrompt(autocompleteInput: AutocompleteInput, prefix: string, suffix: string, languageId: string): string {
let prompt = ""

// Start with recent typing context
if (context.recentOperations && context.recentOperations.length > 0) {
// Start with recent typing context from autocompleteInput
if (autocompleteInput.recentlyEditedRanges && autocompleteInput.recentlyEditedRanges.length > 0) {
prompt += "## Recent Typing\n"
context.recentOperations.forEach((op, index) => {
prompt += `${index + 1}. ${op.description}\n`
autocompleteInput.recentlyEditedRanges.forEach((range, index) => {
const description = `Edited ${range.filepath} at line ${range.range.start.line}`
prompt += `${index + 1}. ${description}\n`
})
prompt += "\n"
}

// Add current position
if (context.range && context.document) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it removes these ifs, but i do not think they were ever necessary tbh

const line = context.range.start.line + 1
const char = context.range.start.character + 1
prompt += `## Current Position\n`
prompt += `Line ${line}, Character ${char}\n\n`
}
// Add current position from autocompleteInput
const line = autocompleteInput.pos.line + 1
const char = autocompleteInput.pos.character + 1
prompt += `## Current Position\n`
prompt += `Line ${line}, Character ${char}\n\n`

// Add the full document with cursor marker
if (context.document) {
prompt += "## Full Code\n"
prompt += formatDocumentWithCursor(context.document, context.range)
prompt += "\n\n"
}
const codeWithCursor = `${prefix}${CURSOR_MARKER}${suffix}`
prompt += "## Full Code\n"
prompt += `\`\`\`${languageId}\n${codeWithCursor}\n\`\`\`\n\n`

// Add specific instructions
prompt += "## Instructions\n"
Expand Down Expand Up @@ -189,23 +179,29 @@ Provide non-intrusive completions after a typing pause. Be conservative and help
)
}

getCommentsUserPrompt(context: GhostSuggestionContext): string {
if (!context.document || !context.range) {
return "No context available for comment-driven generation."
}
getCommentsUserPrompt(prefix: string, suffix: string, languageId: string): string {
// Extract the comment from the prefix
const lines = prefix.split("\n")
const lastLine = lines[lines.length - 1]
const previousLine = lines.length > 1 ? lines[lines.length - 2] : ""

const language = context.document.languageId
const comment = cleanComment(extractComment(context.document, context.range.start.line), language)
// Determine which line contains the comment
const commentLine = isCommentLine(lastLine, languageId) ? lastLine : previousLine
const comment = cleanComment(commentLine, languageId)

const codeWithCursor = `${prefix}${CURSOR_MARKER}${suffix}`

let prompt = `## Comment-Driven Development
- Language: ${language}
- Language: ${languageId}
- Comment to Implement:
\`\`\`
${comment}
\`\`\`

## Full Code
${formatDocumentWithCursor(context.document, context.range)}
\`\`\`${languageId}
${codeWithCursor}
\`\`\`

## Instructions
Generate code that implements the functionality described in the comment.
Expand Down
50 changes: 0 additions & 50 deletions src/services/ghost/strategies/CommentHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,56 +38,6 @@ export function isCommentLine(line: string, languageId: string): boolean {
return false
}

export function extractComment(document: TextDocument, currentLine: number): string {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unrelated, but when i removed use of another import of autotrigger strategy i noticed this was unused as well

let comment = ""

// Get the comment (could be multi-line)
let commentStartLine = currentLine
let commentEndLine = currentLine

// Check if current line is a comment
const currentLineText = document.lineAt(currentLine).text
if (isCommentLine(currentLineText.trim(), document.languageId)) {
comment = currentLineText.trim()

// Check for multi-line comments above
let line = currentLine - 1
while (line >= 0) {
const lineText = document.lineAt(line).text.trim()
if (isCommentLine(lineText, document.languageId)) {
comment = lineText + "\n" + comment
commentStartLine = line
line--
} else {
break
}
}

// Check for multi-line comments below
line = currentLine + 1
while (line < document.lineCount) {
const lineText = document.lineAt(line).text.trim()
if (isCommentLine(lineText, document.languageId)) {
comment = comment + "\n" + lineText
commentEndLine = line
line++
} else {
break
}
}
} else if (currentLine > 0) {
// Check previous line for comment
const prevLineText = document.lineAt(currentLine - 1).text
if (isCommentLine(prevLineText.trim(), document.languageId)) {
comment = prevLineText.trim()
commentStartLine = currentLine - 1
commentEndLine = currentLine - 1
}
}

return comment
}

/**
* Cleans comment text by removing comment syntax
*/
Expand Down
Loading