{"repo": "bchapuis/brokk", "files": {"src/main/java/io/github/jbellis/brokk/Models.java": {"6c897c986b749b831dcc3a9a1350dd2c85dcb1c8": {"t": 1785745670.3695407, "n": 27465, "h": "45700345a740fda3", "c": "package io.github.jbellis.brokk;\n\nimport dev.langchain4j.agent.tool.ToolSpecification;\nimport dev.langchain4j.data.message.AiMessage;\nimport dev.langchain4j.data.message.ChatMessage;\nimport dev.langchain4j.data.message.SystemMessage;\nimport dev.langchain4j.data.message.UserMessage;\nimport dev.langchain4j.model.StreamingResponseHandler;\nimport dev.langchain4j.model.anthropic.AnthropicChatModel;\nimport dev.langchain4j.model.anthropic.AnthropicStreamingChatModel;\nimport dev.langchain4j.model.chat.ChatLanguageModel;\nimport dev.langchain4j.model.chat.StreamingChatLanguageModel;\nimport dev.langchain4j.model.openai.OpenAiChatModel;\nimport dev.langchain4j.model.openai.OpenAiChatRequestParameters;\nimport dev.langchain4j.model.openai.OpenAiStreamingChatModel;\nimport dev.langchain4j.model.openai.OpenAiTokenizer;\nimport dev.langchain4j.model.output.Response;\nimport com.fasterxml.jackson.databind.JsonNode;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport okhttp3.MediaType;\nimport okhttp3.MultipartBody;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Request;\nimport okhttp3.RequestBody;\nimport org.apache.logging.log4j.LogManager;\nimport org.apache.logging.log4j.Logger;\nimport org.yaml.snakeyaml.Yaml;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.http.HttpResponse;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.time.Duration;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.TimeUnit;\nimport java.util.function.Consumer;\n\n/**\n * Holds references to the models we use in Brokk.\n */\npublic record Models(StreamingChatLanguageModel editModel,\n StreamingChatLanguageModel applyModel,\n ChatLanguageModel quickModel,\n ChatLanguageModel searchModel,\n String editModelName,\n String applyModelName,\n String quickModelName,\n String searchModelName,\n SpeechToTextModel sttModel,\n String sttModelName)\n{\n public static String[] defaultKeyNames = { \"ANTHROPIC_API_KEY\", \"OPENAI_API_KEY\", \"DEEPSEEK_API_KEY\" };\n\n /**\n * Returns the name of the given model.\n * \n * @param model The model to get the name for\n * @return The name of the model, or \"unknown\" if the model is not recognized\n */\n public String nameOf(Object model) {\n if (model == editModel) return editModelName;\n if (model == applyModel) return applyModelName;\n if (model == quickModel) return quickModelName;\n if (model == searchModel) return searchModelName;\n return \"unknown\";\n }\n // langchain4j only supports openai tokenization, this is not very accurate for other providers\n // but doing loc-based estimation based on information in the responses was worse\n private static final OpenAiTokenizer tokenizer = new OpenAiTokenizer(\"o3-mini\");\n\n // correct for these models\n private static final int DEFAULT_MAX_TOKENS = 8192;\n\n private static final String OPENAI_DEFAULTS = \"\"\"\n edit_model:\n provider: openai\n key: OPENAI_API_KEY\n name: o3-mini\n reasoning_effort: low\n maxTokens: 100000\n \n search_model:\n provider: openai\n key: OPENAI_API_KEY\n name: o3-mini\n reasoning_effort: low\n maxTokens: 100000\n\n apply_model:\n provider: openai\n key: OPENAI_API_KEY\n name: o3-mini\n reasoning_effort: low\n maxTokens: 100000\n\n quick_model:\n provider: openai\n key: OPENAI_API_KEY\n name: gpt-4o-mini\n maxTokens: 16384\n \"\"\";\n\n private static final String ANTHROPIC_DEFAULTS = \"\"\"\n edit_model:\n provider: anthropic\n key: ANTHROPIC_API_KEY\n name: claude-3-7-sonnet-latest\n maxTokens: 8192\n\n search_model:\n provider: anthropic\n key: ANTHROPIC_API_KEY\n name: claude-3-7-sonnet-latest\n enableCaching: true\n maxTokens: 8192\n\n apply_model:\n provider: anthropic\n key: ANTHROPIC_API_KEY\n name: claude-3-5-haiku-latest\n maxTokens: 8192\n\n quick_model:\n provider: anthropic\n key: ANTHROPIC_API_KEY\n name: claude-3-haiku-20240307\n maxTokens: 4096\n \n \"\"\";\n\n private static final String DEEPSEEK_DEFAULTS = \"\"\"\n edit_model:\n provider: deepseek\n key: DEEPSEEK_API_KEY\n name: deepseek-reasoner\n maxTokens: 8192\n\n search_model:\n provider: deepseek\n key: DEEPSEEK_API_KEY\n name: deepseek-chat\n maxTokens: 8192\n\n apply_model:\n provider: deepseek\n key: DEEPSEEK_API_KEY\n name: deepseek-chat\n maxTokens: 8192\n\n quick_model:\n provider: deepseek\n key: DEEPSEEK_API_KEY\n name: deepseek-chat\n maxTokens: 8192\n \"\"\";\n\n /**\n * Attempts to load model configurations from ~/.config/brokk/brokk.yaml.\n *
\n * The YAML filename define three models: edit_model, apply_model, and quick_model.\n * If any model definition is missing or\n * the filename cannot be read or parsed, default settings are used.\n *
\n * Example YAML structure:\n *
\n * edit_model:\n * provider: deepseek\n * key: DEEPSEEK_API_KEY\n * url: https://api.deepseek.com\n * name: deepseek-chat\n * temperature: 0.4\n * maxTokens: 8000\n *\n *
openai, deepseek, anthropic). If omitted, the code falls back\n * to interpreting url + key as a custom OpenAI-like endpoint or uses\n * a built-in OpenAI default.DEEPSEEK_API_KEY).\n * This is looked up via ~/.config/brokk/keys.properties, or by System.getenv.\n * deepseek, for instance, defaults to\n * https://api.deepseek.com unless otherwise specified. OpenAI can omit url\n * to use the default OpenAI endpoint.gpt-3.5-turbo, deepseek-chat,\n * claude-v1.3, etc.).~/.config/brokk/brokk.yaml.\n * \n * The YAML filename define three models: edit_model, apply_model, and quick_model.\n * If any model definition is missing or\n * the filename cannot be read or parsed, default settings are used.\n *
\n * Example YAML structure:\n *
\n * edit_model:\n * provider: deepseek\n * key: DEEPSEEK_API_KEY\n * url: https://api.deepseek.com\n * name: deepseek-chat\n * temperature: 0.4\n * maxTokens: 8000\n *\n *
openai, deepseek, anthropic). If omitted, the code falls back\n * to interpreting url + key as a custom OpenAI-like endpoint or uses\n * a built-in OpenAI default.DEEPSEEK_API_KEY).\n * This is looked up via ~/.config/brokk/keys.properties, or by System.getenv.\n * deepseek, for instance, defaults to\n * https://api.deepseek.com unless otherwise specified. OpenAI can omit url\n * to use the default OpenAI endpoint.gpt-3.5-turbo, deepseek-chat,\n * claude-v1.3, etc.).~/.config/brokk/brokk.yaml.\n * \n * The YAML filename define three models: edit_model, apply_model, and quick_model.\n * If any model definition is missing or\n * the filename cannot be read or parsed, default settings are used.\n *
\n * Example YAML structure:\n *
\n * edit_model:\n * provider: deepseek\n * key: DEEPSEEK_API_KEY\n * url: https://api.deepseek.com\n * name: deepseek-chat\n * temperature: 0.4\n * maxTokens: 8000\n *\n *
openai, deepseek, anthropic). If omitted, the code falls back\n * to interpreting url + key as a custom OpenAI-like endpoint or uses\n * a built-in OpenAI default.DEEPSEEK_API_KEY).\n * This is looked up via ~/.config/brokk/keys.properties, or by System.getenv.\n * deepseek, for instance, defaults to\n * https://api.deepseek.com unless otherwise specified. OpenAI can omit url\n * to use the default OpenAI endpoint.gpt-3.5-turbo, deepseek-chat,\n * claude-v1.3, etc.).~/.config/brokk/brokk.yaml.\n * \n * The YAML filename define three models: edit_model, apply_model, and quick_model.\n * If any model definition is missing or\n * the filename cannot be read or parsed, default settings are used.\n *
\n * Example YAML structure:\n *
\n * edit_model:\n * provider: deepseek\n * key: DEEPSEEK_API_KEY\n * url: https://api.deepseek.com\n * name: deepseek-chat\n * temperature: 0.4\n * maxTokens: 8000\n *\n *
openai, deepseek, anthropic). If omitted, the code falls back\n * to interpreting url + key as a custom OpenAI-like endpoint or uses\n * a built-in OpenAI default.DEEPSEEK_API_KEY).\n * This is looked up via ~/.config/brokk/keys.properties, or by System.getenv.\n * deepseek, for instance, defaults to\n * https://api.deepseek.com unless otherwise specified. OpenAI can omit url\n * to use the default OpenAI endpoint.gpt-3.5-turbo, deepseek-chat,\n * claude-v1.3, etc.).~/.config/brokk/brokk.yaml.\n * \n * The YAML filename define three models: edit_model, apply_model, and quick_model.\n * If any model definition is missing or\n * the filename cannot be read or parsed, default settings are used.\n *
\n * Example YAML structure:\n *
\n * edit_model:\n * provider: deepseek\n * key: DEEPSEEK_API_KEY\n * url: https://api.deepseek.com\n * name: deepseek-chat\n * temperature: 0.4\n * maxTokens: 8000\n *\n *
openai, deepseek, anthropic). If omitted, the code falls back\n * to interpreting url + key as a custom OpenAI-like endpoint or uses\n * a built-in OpenAI default.DEEPSEEK_API_KEY).\n * This is looked up via System.getenv. If key is env:SOME_VAR,\n * the portion after env: is taken as the environment variable name. Otherwise, the entire\n * key string is treated as the environment variable name directly.deepseek, for instance, defaults to\n * https://api.deepseek.com unless otherwise specified. OpenAI can omit url\n * to use the default OpenAI endpoint.gpt-3.5-turbo, deepseek-chat,\n * claude-v1.3, etc.).~/.config/brokk/brokk.yaml.\n * \n * The YAML filename define three models: edit_model, apply_model, and quick_model.\n * If any model definition is missing or\n * the filename cannot be read or parsed, default settings are used.\n *
\n * Example YAML structure:\n *
\n * edit_model:\n * provider: deepseek\n * key: DEEPSEEK_API_KEY\n * url: https://api.deepseek.com\n * name: deepseek-chat\n * temperature: 0.4\n * maxTokens: 8000\n *\n *
openai, deepseek, anthropic). If omitted, the code falls back\n * to interpreting url + key as a custom OpenAI-like endpoint or uses\n * a built-in OpenAI default.DEEPSEEK_API_KEY).\n * This is looked up via System.getenv. If key is env:SOME_VAR,\n * the portion after env: is taken as the environment variable name. Otherwise, the entire\n * key string is treated as the environment variable name directly.deepseek, for instance, defaults to\n * https://api.deepseek.com unless otherwise specified. OpenAI can omit url\n * to use the default OpenAI endpoint.gpt-3.5-turbo, deepseek-chat,\n * claude-v1.3, etc.).~/.config/brokk/brokk.yaml.\n * \n * The YAML filename define three models: edit_model, apply_model, and quick_model.\n * If any model definition is missing or\n * the filename cannot be read or parsed, default settings are used.\n *
\n * Example YAML structure:\n *
\n * edit_model:\n * provider: deepseek\n * key: DEEPSEEK_API_KEY\n * url: https://api.deepseek.com\n * name: deepseek-chat\n * temperature: 0.4\n * maxTokens: 8000\n *\n *
openai, deepseek, anthropic). If omitted, the code falls back\n * to interpreting url + key as a custom OpenAI-like endpoint or uses\n * a built-in OpenAI default.DEEPSEEK_API_KEY).\n * This is looked up via System.getenv. If key is env:SOME_VAR,\n * the portion after env: is taken as the environment variable name. Otherwise, the entire\n * key string is treated as the environment variable name directly.deepseek, for instance, defaults to\n * https://api.deepseek.com unless otherwise specified. OpenAI can omit url\n * to use the default OpenAI endpoint.gpt-3.5-turbo, deepseek-chat,\n * claude-v1.3, etc.).~/.config/brokk/brokk.yaml.\n * \n * The YAML filename define three models: edit_model, apply_model, and quick_model.\n * If any model definition is missing or\n * the filename cannot be read or parsed, default settings are used.\n *
\n * Example YAML structure:\n *
\n * edit_model:\n * provider: deepseek\n * key: DEEPSEEK_API_KEY\n * url: https://api.deepseek.com\n * name: deepseek-chat\n * temperature: 0.4\n * maxTokens: 8000\n *\n *
openai, deepseek, anthropic). If omitted, the code falls back\n * to interpreting url + key as a custom OpenAI-like endpoint or uses\n * a built-in OpenAI default.DEEPSEEK_API_KEY).\n * This is looked up via System.getenv. If key is env:SOME_VAR,\n * the portion after env: is taken as the environment variable name. Otherwise, the entire\n * key string is treated as the environment variable name directly.deepseek, for instance, defaults to\n * https://api.deepseek.com unless otherwise specified. OpenAI can omit url\n * to use the default OpenAI endpoint.gpt-3.5-turbo, deepseek-chat,\n * claude-v1.3, etc.).~/.config/brokk/brokk.yaml.\n * \n * The YAML filename define three models: edit_model, apply_model, and quick_model.\n * If any model definition is missing or\n * the filename cannot be read or parsed, default settings are used.\n *
\n * Example YAML structure:\n *
\n * edit_model:\n * provider: deepseek\n * key: DEEPSEEK_API_KEY\n * url: https://api.deepseek.com\n * name: deepseek-chat\n * temperature: 0.4\n * maxTokens: 8000\n *\n *
openai, deepseek, anthropic). If omitted, the code falls back\n * to interpreting url + key as a custom OpenAI-like endpoint or uses\n * a built-in OpenAI default.DEEPSEEK_API_KEY).\n * This is looked up via System.getenv. If key is env:SOME_VAR,\n * the portion after env: is taken as the environment variable name. Otherwise, the entire\n * key string is treated as the environment variable name directly.deepseek, for instance, defaults to\n * https://api.deepseek.com unless otherwise specified. OpenAI can omit url\n * to use the default OpenAI endpoint.gpt-3.5-turbo, deepseek-chat,\n * claude-v1.3, etc.).