prompts.gleam

 1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
 2//
 3// SPDX-License-Identifier: AGPL-3.0-or-later
 4
 5import gleam/option.{type Option, None, Some}
 6import gleam/string
 7
 8/// The system prompt that establishes garble's role as a text transformer.
 9const system_prompt = "You are a text transformation tool. Transform the text in <raw> according to the user's directions.
10
11<rules>
12- Follow the user's transformation directions precisely
13- If no directions are provided, return the input unchanged
14- Output ONLY the transformed text—no explanations, commentary, or metadata
15- Never reference the original text or the fact that a transformation occurred
16- Wrap your entire output in a fenced code block (```)
17</rules>"
18
19/// Returns the system prompt.
20pub fn system() -> String {
21  system_prompt
22}
23
24/// Build the user message with raw input sandwiched between direction references.
25pub fn build_user_message(raw_input: String, directions: String) -> String {
26  let directions_block = case string.trim(directions) {
27    "" -> ""
28    trimmed -> "\n\n<directions>\n" <> trimmed <> "\n</directions>"
29  }
30
31  "<raw>\n"
32  <> raw_input
33  <> "\n</raw>"
34  <> directions_block
35  <> "\n\nTransform the text in <raw> according to the directions above."
36}
37
38/// Extract content from within a fenced code block.
39/// Returns the content inside the first code block found, or the full text if none.
40pub fn extract_code_block(text: String) -> String {
41  case find_code_block(text) {
42    Some(content) -> content
43    None -> text
44  }
45}
46
47fn find_code_block(text: String) -> Option(String) {
48  case string.split_once(text, "```") {
49    Error(_) -> None
50    Ok(#(_before, after_open)) -> {
51      // Skip the language tag (everything until first newline)
52      let content_start = case string.split_once(after_open, "\n") {
53        Ok(#(_lang, rest)) -> rest
54        Error(_) -> after_open
55      }
56      case string.split_once(content_start, "```") {
57        Ok(#(content, _after_close)) -> Some(string.trim(content))
58        Error(_) -> None
59      }
60    }
61  }
62}