Skip to content

Latest commit

 

History

History
360 lines (288 loc) · 11.3 KB

File metadata and controls

360 lines (288 loc) · 11.3 KB

FIM LLM Prompt Structure

The prompt sent to the FIM LLM follows this structure:

'(:template (:prompt minuet--default-fim-prompt-function
             :suffix minuet--default-fim-suffix-function))

This template utilizes two default functions that generate the following:

  • :prompt: Returns the programming language, indentation style, and the verbatim content of the context before the cursor.
  • :suffix: Returns the verbatim content of the context after cursor.

Both :prompt and :suffix are implemented as functions, each accepting a plist ctx with these attributes and returning a string:

  • :before-cursor: The text before the cursor.
  • :after-cursor: The text after the cursor.
  • :language-and-tab: The programming language and indentation style.
  • is_incomplete_before: A boolean indicating whether the content before the cursor was truncated.
  • is_incomplete_after: A boolean indicating whether the content after the cursor was truncated.

These functions are customizable to provide additional context to the LLM. The :suffix function can be disabled by setting its value to nil using plist-put, resulting in a request containing only the :prompt content.

Note: for Ollama users: Do not include special tokens (e.g., <|fim_begin|>) within the prompt or suffix functions, as these will be automatically populated by Ollama. If your use case requires special tokens not covered by Ollama's default template, disable the :suffix function by setting it to nil and incorporate the necessary special tokens within the prompt function.

Chat LLM Prompt Structure

We utilize two distinct strategies when constructing prompts:

  1. Prefix First Style: This involves including the code preceding the cursor initially, followed by the code succeeding the cursor. This approach is used for the OpenAI and Gemini providers.

  2. Suffix First Style: This method involves including the code following the cursor initially, and then the code preceding the cursor. It is employed for OpenAI-Compatible and Claude providers.

The counterpart variables are:

  1. minuet-default-prompt and minuet-default-prompt-prefix-first.
  2. minuet-default-fewshots and minuet-default-fewshots-prefix-first.
  3. minuet-default-chat-input-template and minuet-default-chat-input-template-prefix-first.

Default Template

{{{:prompt}}}\n{{{:guidelines}}}\n{{{:n_completion_template}}}

Default Prompt

Prefix First Style:

You are an AI code completion engine. Provide contextually appropriate completions:

  • Code completions in code context
  • Comment/documentation text in comments
  • String content in string literals
  • Prose in markdown/documentation files

Input markers:

  • <contextAfterCursor>: Context after cursor
  • <cursorPosition>: Current cursor location
  • <contextBeforeCursor>: Context before cursor

Suffix First Style:

You are an AI code completion engine. Provide contextually appropriate completions:

  • Code completions in code context
  • Comment/documentation text in comments
  • String content in string literals
  • Prose in markdown/documentation files

Input markers:

  • <contextAfterCursor>: Context after cursor
  • <cursorPosition>: Current cursor location
  • <contextBeforeCursor>: Context before cursor

Note that the user input will be provided in reverse order: first the context after cursor, followed by the context before cursor.

Default Guidelines

Guidelines:

  1. Offer completions after the <cursorPosition> marker.
  2. Make sure you have maintained the user's existing whitespace and indentation. This is REALLY IMPORTANT!
  3. Provide multiple completion options when possible.
  4. Return completions separated by the marker <endCompletion>.
  5. The returned message will be further parsed and processed. DO NOT include additional comments or markdown code block fences. Return the result directly.
  6. Keep each completion option concise, limiting it to a single line or a few lines.
  7. Create entirely new code completion that DO NOT REPEAT OR COPY any user's existing code around <cursorPosition>.

Default :n_completions template

  1. Provide at most %d completion items.

Default Few Shots Examples

;; suffix-first style
(defvar minuet-default-fewshots
  `((:role "user"
     :content "# language: javascript
<contextAfterCursor>
    return result;
}

const processedData = transformData(rawData, {
    uppercase: true,
    removeSpaces: false
});
<contextBeforeCursor>
function transformData(data, options) {
    const result = [];
    for (let item of data) {
        <cursorPosition>")
    (:role "assistant"
     :content "let processed = item;
        if (options.uppercase) {
            processed = processed.toUpperCase();
        }
        if (options.removeSpaces) {
            processed = processed.replace(/\s+/g, '');
        }
        result.push(processed);
    }
<endCompletion>
if (typeof item === 'string') {
            let processed = item;
            if (options.uppercase) {
                processed = processed.toUpperCase();
            }
            if (options.removeSpaces) {
                processed = processed.replace(/\s+/g, '');
            }
            result.push(processed);
        } else {
            result.push(item);
        }
    }
<endCompletion>
")))

(defvar minuet-default-fewshots-prefix-first
  `((:role "user"
     :content "# language: javascript
<contextBeforeCursor>
function transformData(data, options) {
    const result = [];
    for (let item of data) {
        <cursorPosition>
<contextAfterCursor>
    return result;
}

const processedData = transformData(rawData, {
    uppercase: true,
    removeSpaces: false
});")
    ,(cadr minuet-default-fewshots)))

Default Chat Input Example

The chat input represents the final prompt delivered to the LLM for completion.

The chat input template follows a structure similar to the system prompt and can be customized using the following format:

Suffix First Style:

{{{:language-and-tab}}}
<contextAfterCursor>
{{{:context_after_cursor}}}
<contextBeforeCursor>
{{{:context_before_cursor}}}<cursorPosition>

Prefix First Style:

{{{:language-and-tab}}}
<contextBeforeCursor>
{{{:context_before_cursor}}}<cursorPosition>
<contextAfterCursor>
{{{:context_after_cursor}}}

The chat input template can be provided either as a single string or as a list of strings. If supplied as a list, each string will be expanded using the template and its components. The resulting list will then be transformed into a multi-turn conversation, with roles alternating between user and assistant.

Components:

  • :language-and-tab: Specifies the programming language and indentation style utilized by the user
  • :context-before-cursor: Contains the text content preceding the cursor position
  • :context-after-cursor: Contains the text content following the cursor position

Implementation requires each component to be defined by a function that accepts a single parameter context and returns a string. This context parameter is a plist containing the following values:

  • :before-cursor: The text before the cursor.
  • :after-cursor: The text after the cursor.
  • :language-and-tab: The programming language and indentation style.
  • is_incomplete_before: A boolean indicating whether the content before the cursor was truncated.
  • is_incomplete_after: A boolean indicating whether the content after the cursor was truncated.

Customization

You can customize the :template by encoding placeholders within triple braces. These placeholders will be interpolated using the corresponding key-value pairs from the table. The value can be a function that takes no argument and returns a string, or a symbol whose value is a string.

Here's a simplified example for illustrative purposes (not intended for actual configuration):

(setq my-minuet-simple-template "{{{:assistant}}}\n{{{:role}}}")
(setq my-minuet-simple-role "you are also a computer scientist")
(defun my-simple-assistant-prompt () "" "you are a helpful assistant.")

(plist-put
 minuet-openai-options
 :system
 '(:template my-minuet-simple-template ; note: you do not need the comma , for interpolation
   :assistant my-simple-assistant-prompt
   :role my-minuet-simple-role))

Note that :n_completion_template is a special placeholder as it contains one %d which will be encoded with minuet-n-completions, if you want to customize this template, make sure your prompt also contains only one %d.

Similarly, :fewshots can be a plist in the following form or a function that takes no argument and returns a plist.

Below is an example to configure the prompt based on major mode:

(defun my-minuet-few-shots ()
    (if (derived-mode-p 'js-mode)
            (list '(:role "user"
                    :content "// language: javascript
<contextAfterCursor>

fib(5)
<contextBeforeCursor>
function fibonacci(n) {
    <cursorPosition>")
                  '(:role "assistant"
                    :content "    // Recursive Fibonacci implementation
    if (n < 2) {
        return n;
    }
    return fibonacci(n - 1) + fibonacci(n - 2);
<endCompletion>
    // Iterative Fibonacci implementation
    let a = 0, b = 1;
    for (let i = 0; i < n; i++) {
        [a, b] = [b, a + b];
    }
    return a;
<endCompletion>
"))
        minuet-default-fewshots))

(plist-put minuet-openai-options :fewshots #'my-minuet-few-shots)

A Practical Example

Here, we present a practical example for configuring the prompt for Gemini, aiming to reuse existing components of the default prompt wherever possible.

Please note that you should not copy-paste this into your configuration, as it represents the default setting applied to Gemini.

(use-package minuet
  :config
  (setq minuet-provider 'gemini)

  (defvar my-minuet-gemini-prompt minuet-default-prompt-prefix-first)

  (defvar my-minuet-gemini-chat-input-template
    "{{{:language-and-tab}}}
<contextBeforeCursor>
{{{:context-before-cursor}}}<cursorPosition>
<contextAfterCursor>
{{{:context-after-cursor}}}")

  (defvar my-minuet-gemini-fewshots
    `((:role "user"
       :content "# language: javascript
<contextBeforeCursor>
function transformData(data, options) {
    const result = [];
    for (let item of data) {
        <cursorPosition>
<contextAfterCursor>
    return result;
}

const processedData = transformData(rawData, {
    uppercase: true,
    removeSpaces: false
});")
      ,(cadr minuet-default-fewshots)))

  (minuet-set-optional-options minuet-gemini-options
                               :prompt 'my-minuet-gemini-prompt
                               :system)
  (minuet-set-optional-options minuet-gemini-options
                               :template 'my-minuet-gemini-chat-input-template
                               :chat-input)
  (plist-put minuet-gemini-options :fewshots 'my-minuet-gemini-fewshots)
  )