mindcraft 0.1.4-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/FAQ.md +38 -0
  2. package/LICENSE +21 -0
  3. package/README.md +255 -0
  4. package/andy.json +6 -0
  5. package/bin/mindcraft.js +80 -0
  6. package/keys.example.json +19 -0
  7. package/main.js +80 -0
  8. package/package.json +78 -0
  9. package/patches/minecraft-data+3.97.0.patch +13 -0
  10. package/patches/mineflayer+4.33.0.patch +54 -0
  11. package/patches/mineflayer-pathfinder+2.4.5.patch +265 -0
  12. package/patches/mineflayer-pvp+1.3.2.patch +13 -0
  13. package/patches/prismarine-viewer+1.33.0.patch +13 -0
  14. package/patches/protodef+1.19.0.patch +15 -0
  15. package/profiles/andy-4-reasoning.json +14 -0
  16. package/profiles/andy-4.json +7 -0
  17. package/profiles/azure.json +19 -0
  18. package/profiles/claude.json +7 -0
  19. package/profiles/claude_thinker.json +15 -0
  20. package/profiles/deepseek.json +7 -0
  21. package/profiles/defaults/_default.json +256 -0
  22. package/profiles/defaults/assistant.json +14 -0
  23. package/profiles/defaults/creative.json +14 -0
  24. package/profiles/defaults/god_mode.json +14 -0
  25. package/profiles/defaults/survival.json +14 -0
  26. package/profiles/freeguy.json +7 -0
  27. package/profiles/gemini.json +9 -0
  28. package/profiles/gpt.json +12 -0
  29. package/profiles/grok.json +7 -0
  30. package/profiles/llama.json +10 -0
  31. package/profiles/mercury.json +9 -0
  32. package/profiles/mistral.json +5 -0
  33. package/profiles/qwen.json +17 -0
  34. package/profiles/tasks/construction_profile.json +42 -0
  35. package/profiles/tasks/cooking_profile.json +11 -0
  36. package/profiles/tasks/crafting_profile.json +71 -0
  37. package/profiles/vllm.json +10 -0
  38. package/settings.js +64 -0
  39. package/src/agent/action_manager.js +177 -0
  40. package/src/agent/agent.js +561 -0
  41. package/src/agent/coder.js +229 -0
  42. package/src/agent/commands/actions.js +504 -0
  43. package/src/agent/commands/index.js +259 -0
  44. package/src/agent/commands/queries.js +347 -0
  45. package/src/agent/connection_handler.js +96 -0
  46. package/src/agent/conversation.js +353 -0
  47. package/src/agent/history.js +122 -0
  48. package/src/agent/library/full_state.js +89 -0
  49. package/src/agent/library/index.js +23 -0
  50. package/src/agent/library/lockdown.js +32 -0
  51. package/src/agent/library/skill_library.js +93 -0
  52. package/src/agent/library/skills.js +2093 -0
  53. package/src/agent/library/world.js +431 -0
  54. package/src/agent/memory_bank.js +25 -0
  55. package/src/agent/mindserver_proxy.js +136 -0
  56. package/src/agent/modes.js +446 -0
  57. package/src/agent/npc/build_goal.js +80 -0
  58. package/src/agent/npc/construction/dirt_shelter.json +38 -0
  59. package/src/agent/npc/construction/large_house.json +230 -0
  60. package/src/agent/npc/construction/small_stone_house.json +42 -0
  61. package/src/agent/npc/construction/small_wood_house.json +42 -0
  62. package/src/agent/npc/controller.js +261 -0
  63. package/src/agent/npc/data.js +50 -0
  64. package/src/agent/npc/item_goal.js +355 -0
  65. package/src/agent/npc/utils.js +126 -0
  66. package/src/agent/self_prompter.js +146 -0
  67. package/src/agent/settings.js +7 -0
  68. package/src/agent/speak.js +150 -0
  69. package/src/agent/tasks/construction_tasks.js +1104 -0
  70. package/src/agent/tasks/cooking_tasks.js +358 -0
  71. package/src/agent/tasks/tasks.js +594 -0
  72. package/src/agent/templates/execTemplate.js +6 -0
  73. package/src/agent/templates/lintTemplate.js +10 -0
  74. package/src/agent/vision/browser_viewer.js +8 -0
  75. package/src/agent/vision/camera.js +78 -0
  76. package/src/agent/vision/vision_interpreter.js +82 -0
  77. package/src/mindcraft/index.js +28 -0
  78. package/src/mindcraft/mcserver.js +154 -0
  79. package/src/mindcraft/mindcraft.js +111 -0
  80. package/src/mindcraft/mindserver.js +328 -0
  81. package/src/mindcraft/public/index.html +1253 -0
  82. package/src/mindcraft/public/settings_spec.json +145 -0
  83. package/src/mindcraft/userconfig.js +72 -0
  84. package/src/mindcraft-py/example.py +27 -0
  85. package/src/mindcraft-py/init-mindcraft.js +24 -0
  86. package/src/mindcraft-py/mindcraft.py +99 -0
  87. package/src/models/_model_map.js +89 -0
  88. package/src/models/azure.js +32 -0
  89. package/src/models/cerebras.js +61 -0
  90. package/src/models/claude.js +87 -0
  91. package/src/models/deepseek.js +59 -0
  92. package/src/models/gemini.js +176 -0
  93. package/src/models/glhf.js +71 -0
  94. package/src/models/gpt.js +147 -0
  95. package/src/models/grok.js +82 -0
  96. package/src/models/groq.js +95 -0
  97. package/src/models/huggingface.js +86 -0
  98. package/src/models/hyperbolic.js +114 -0
  99. package/src/models/lmstudio.js +74 -0
  100. package/src/models/mercury.js +95 -0
  101. package/src/models/mistral.js +94 -0
  102. package/src/models/novita.js +71 -0
  103. package/src/models/ollama.js +115 -0
  104. package/src/models/openrouter.js +77 -0
  105. package/src/models/prompter.js +366 -0
  106. package/src/models/qwen.js +80 -0
  107. package/src/models/replicate.js +60 -0
  108. package/src/models/vllm.js +81 -0
  109. package/src/process/agent_process.js +84 -0
  110. package/src/process/init_agent.js +54 -0
  111. package/src/utils/examples.js +83 -0
  112. package/src/utils/keys.js +34 -0
  113. package/src/utils/math.js +13 -0
  114. package/src/utils/mcdata.js +572 -0
  115. package/src/utils/text.js +78 -0
  116. package/src/utils/translator.js +30 -0
@@ -0,0 +1,145 @@
1
+ {
2
+ "profile": {
3
+ "type": "object",
4
+ "required": true,
5
+ "description": "The profile object to use, including name, prompts, and examples"
6
+ },
7
+ "data_dir": {
8
+ "type": "string",
9
+ "description": "Directory where bot memories, histories, screenshots, and generated code are written",
10
+ "default": "./bots"
11
+ },
12
+ "minecraft_version": {
13
+ "type": "string",
14
+ "description": "The version of Minecraft to use. Set to 'auto' to automatically detect the version.",
15
+ "default": "auto"
16
+ },
17
+ "host": {
18
+ "type": "string",
19
+ "description": "The minecraft server host address to connect to",
20
+ "default": "127.0.0.1"
21
+ },
22
+ "port": {
23
+ "type": "number",
24
+ "description": "The minecraft server port to connect to. -1 for auto-detect.",
25
+ "default": 55916
26
+ },
27
+ "auth": {
28
+ "type": "string",
29
+ "description": "The authentication method to use",
30
+ "default": "offline",
31
+ "options": ["offline", "microsoft"]
32
+ },
33
+ "base_profile": {
34
+ "type": "string",
35
+ "description": "Allowed values: survival, assistant, creative, god_mode. Each has fine tuned settings for different game modes.",
36
+ "default": "survival",
37
+ "options": ["survival", "assistant", "creative", "god_mode"]
38
+ },
39
+ "load_memory": {
40
+ "type": "boolean",
41
+ "description": "Whether to load bot's previous memory",
42
+ "default": false
43
+ },
44
+ "init_message": {
45
+ "type": "string",
46
+ "description": "The initial message to send to the bot",
47
+ "default": "Respond with hello world and your name"
48
+ },
49
+ "only_chat_with": {
50
+ "type": "array",
51
+ "description": "List of agents to only chat with. If empty, the bot will chat publicly",
52
+ "default": []
53
+ },
54
+ "speak": {
55
+ "type": "boolean",
56
+ "description": "Whether to enable text-to-speech reading on the host machine",
57
+ "default": false
58
+ },
59
+ "language": {
60
+ "type": "string",
61
+ "description": "The language to automatically translate to and from using google translate",
62
+ "default": "en"
63
+ },
64
+ "allow_vision": {
65
+ "type": "boolean",
66
+ "description": "Whether to allow vision capabilities",
67
+ "default": false
68
+ },
69
+ "blocked_actions": {
70
+ "type": "array",
71
+ "description": "List of actions that are blocked",
72
+ "default": ["!checkBlueprint", "!checkBlueprintLevel", "!getBlueprint", "!getBlueprintLevel"]
73
+ },
74
+ "relevant_docs_count": {
75
+ "type": "number",
76
+ "description": "Number of relevant function documents to include in the prompt for LLM code writing",
77
+ "default": 5
78
+ },
79
+ "max_messages": {
80
+ "type": "number",
81
+ "description": "Maximum number of recent messages to keep in context for LLM",
82
+ "default": 15
83
+ },
84
+ "num_examples": {
85
+ "type": "number",
86
+ "description": "Number of examples to select to help prompt better LLM responses",
87
+ "default": 2
88
+ },
89
+ "max_commands": {
90
+ "type": "number",
91
+ "description": "Maximum number of commands allowed in consecutive responses. -1 for no limit",
92
+ "default": -1
93
+ },
94
+ "narrate_behavior": {
95
+ "type": "boolean",
96
+ "description": "Whether to openly chat automatic behavior like 'Picking up item!'",
97
+ "default": true
98
+ },
99
+ "log_all_prompts": {
100
+ "type": "boolean",
101
+ "description": "Whether to log all prompts to file. Can be very verbose.",
102
+ "default": false
103
+ },
104
+ "show_command_syntax": {
105
+ "type": "string",
106
+ "description": "Whether to show \"full\" command syntax, \"shortened\" command syntax, or \"none\"",
107
+ "default": "full",
108
+ "options": ["full", "shortened", "none"]
109
+ },
110
+ "chat_ingame": {
111
+ "type": "boolean",
112
+ "description": "Whether to show bot's chat messages in game chat",
113
+ "default": true
114
+ },
115
+ "chat_bot_messages": {
116
+ "type": "boolean",
117
+ "description": "Whether to publicly chat messages to and from other bots",
118
+ "default": true
119
+ },
120
+ "render_bot_view": {
121
+ "type": "boolean",
122
+ "description": "Whether to render bot view for user observation. Does not give bot vision.",
123
+ "default": false
124
+ },
125
+ "allow_insecure_coding": {
126
+ "type": "boolean",
127
+ "description": "Whether to allow newAction command that let's LLM write/run code on host computer. Despite sandboxxing, it is potentially insecure.",
128
+ "default": false
129
+ },
130
+ "code_timeout_mins": {
131
+ "type": "number",
132
+ "description": "Number of minutes to allow code execution. -1 for no timeout",
133
+ "default": -1
134
+ },
135
+ "task": {
136
+ "type": "object",
137
+ "description": "The task object to give the agent on start. If null, the agent will not have a task.",
138
+ "default": null
139
+ },
140
+ "spawn_timeout": {
141
+ "type": "number",
142
+ "description": "Number of seconds allowed for the bot to spawn before throwing an error. Increase when spawning takes a while.",
143
+ "default": 30
144
+ }
145
+ }
@@ -0,0 +1,72 @@
1
+ import { readFileSync, writeFileSync, mkdirSync, existsSync, readdirSync, chmodSync } from 'fs';
2
+ import { homedir } from 'os';
3
+ import { join } from 'path';
4
+
5
+ // Persistence layer used by the web UI's setup wizard.
6
+ // Keeps API keys, server config, and saved profiles across restarts.
7
+ // Follows the XDG base-dir convention: $XDG_CONFIG_HOME/mindcraft, falling
8
+ // back to ~/.config/mindcraft.
9
+
10
+ const CONFIG_HOME = process.env.XDG_CONFIG_HOME || join(homedir(), '.config');
11
+ const ROOT = join(CONFIG_HOME, 'mindcraft');
12
+ const CONFIG_PATH = join(ROOT, 'config.json');
13
+ const KEYS_PATH = join(ROOT, 'keys.json');
14
+ const PROFILES_DIR = join(ROOT, 'profiles');
15
+ const BOTS_DIR = join(ROOT, 'bots');
16
+
17
+ export const paths = { ROOT, CONFIG_PATH, KEYS_PATH, PROFILES_DIR, BOTS_DIR };
18
+
19
+ function readJson(path) {
20
+ if (!existsSync(path)) return null;
21
+ try { return JSON.parse(readFileSync(path, 'utf8')); }
22
+ catch { return null; }
23
+ }
24
+
25
+ function writeJson(path, data, mode) {
26
+ mkdirSync(ROOT, { recursive: true });
27
+ writeFileSync(path, JSON.stringify(data, null, 2));
28
+ if (mode) chmodSync(path, mode);
29
+ }
30
+
31
+ export function getConfig() {
32
+ return readJson(CONFIG_PATH);
33
+ }
34
+
35
+ export function saveConfig(config) {
36
+ writeJson(CONFIG_PATH, config);
37
+ }
38
+
39
+ export function hasKeys() {
40
+ const k = readJson(KEYS_PATH);
41
+ return !!k && Object.values(k).some(v => v);
42
+ }
43
+
44
+ export function loadKeysIntoEnv() {
45
+ const k = readJson(KEYS_PATH);
46
+ if (!k) return;
47
+ for (const [name, value] of Object.entries(k)) {
48
+ if (value && !process.env[name]) process.env[name] = value;
49
+ }
50
+ }
51
+
52
+ export function saveKeys(keys) {
53
+ const existing = readJson(KEYS_PATH) || {};
54
+ writeJson(KEYS_PATH, { ...existing, ...keys }, 0o600);
55
+ for (const [name, value] of Object.entries(keys)) {
56
+ if (value) process.env[name] = value;
57
+ }
58
+ }
59
+
60
+ export function listProfiles() {
61
+ if (!existsSync(PROFILES_DIR)) return [];
62
+ return readdirSync(PROFILES_DIR)
63
+ .filter(f => f.endsWith('.json'))
64
+ .map(f => readJson(join(PROFILES_DIR, f)))
65
+ .filter(p => p && p.name);
66
+ }
67
+
68
+ export function saveProfile(profile) {
69
+ if (!profile?.name) throw new Error('profile.name is required');
70
+ mkdirSync(PROFILES_DIR, { recursive: true });
71
+ writeJson(join(PROFILES_DIR, `${profile.name}.json`), profile);
72
+ }
@@ -0,0 +1,27 @@
1
+ import mindcraft
2
+ import json
3
+ import os
4
+
5
+ # Initialize Mindcraft, starting the Node.js server
6
+ # This will also connect to the MindServer via websockets
7
+ mindcraft.init()
8
+
9
+ # Get the directory of the current script
10
+ script_dir = os.path.dirname(os.path.abspath(__file__))
11
+ profile_path = os.path.abspath(os.path.join(script_dir, '..', '..', 'andy.json'))
12
+
13
+ # Load agent settings from a JSON file
14
+ try:
15
+ with open(profile_path, 'r') as f:
16
+ profile_data = json.load(f)
17
+
18
+ settings = {"profile": profile_data}
19
+ mindcraft.create_agent(settings)
20
+
21
+ settings_copy = settings.copy()
22
+ settings_copy['profile']['name'] = 'andy2'
23
+ mindcraft.create_agent(settings_copy)
24
+ except FileNotFoundError:
25
+ print(f"Error: Could not find andy.json at {profile_path}")
26
+
27
+ mindcraft.wait()
@@ -0,0 +1,24 @@
1
+ import * as Mindcraft from '../mindcraft/mindcraft.js';
2
+ import settings from '../../settings.js';
3
+ import yargs from 'yargs';
4
+ import { hideBin } from 'yargs/helpers';
5
+
6
+ function parseArguments() {
7
+ return yargs(hideBin(process.argv))
8
+ .option('mindserver_port', {
9
+ type: 'number',
10
+ describe: 'Mindserver port',
11
+ default: settings.mindserver_port
12
+ })
13
+ .help()
14
+ .alias('help', 'h')
15
+ .parse();
16
+ }
17
+
18
+ const args = parseArguments();
19
+
20
+ settings.mindserver_port = args.mindserver_port;
21
+
22
+ Mindcraft.init(settings.mindserver_port);
23
+
24
+ console.log(`Mindcraft initialized with MindServer at localhost:${settings.mindserver_port}`);
@@ -0,0 +1,99 @@
1
+ import subprocess
2
+ import socketio
3
+ import time
4
+ import json
5
+ import os
6
+ import atexit
7
+ import threading
8
+ import sys
9
+ import signal
10
+
11
+ class Mindcraft:
12
+ def __init__(self):
13
+ self.sio = socketio.Client()
14
+ self.process = None
15
+ self.connected = False
16
+ self.log_thread = None
17
+
18
+ def _log_reader(self):
19
+ for line in iter(self.process.stdout.readline, ''):
20
+ sys.stdout.write(f'[Node.js] {line}')
21
+ sys.stdout.flush()
22
+
23
+ def init(self, port=8080):
24
+ if self.process:
25
+ return
26
+
27
+ self.port = port
28
+
29
+ node_script_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'init-mindcraft.js'))
30
+
31
+ self.process = subprocess.Popen([
32
+ 'node',
33
+ node_script_path,
34
+ '--mindserver_port', str(self.port)
35
+ ], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1)
36
+
37
+ self.log_thread = threading.Thread(target=self._log_reader)
38
+ self.log_thread.daemon = True
39
+ self.log_thread.start()
40
+
41
+ atexit.register(self.shutdown)
42
+ time.sleep(2) # Give server time to start before connecting
43
+
44
+ try:
45
+ self.sio.connect(f'http://localhost:{self.port}')
46
+ self.connected = True
47
+ print("Connected to MindServer. Mindcraft is initialized.")
48
+ except socketio.exceptions.ConnectionError as e:
49
+ print(f"Failed to connect to MindServer: {e}")
50
+ self.shutdown()
51
+ raise
52
+
53
+ def create_agent(self, settings_json):
54
+ if not self.connected:
55
+ raise Exception("Not connected to MindServer. Call init() first.")
56
+
57
+ profile_data = settings_json.get('profile', {})
58
+
59
+ def callback(response):
60
+ if response.get('success'):
61
+ print(f"Agent '{profile_data.get('name')}' created successfully")
62
+ else:
63
+ print(f"Error creating agent: {response.get('error', 'Unknown error')}")
64
+
65
+ self.sio.emit('create-agent', settings_json, callback=callback)
66
+
67
+ def shutdown(self):
68
+ if self.sio.connected:
69
+ self.sio.disconnect()
70
+ self.connected = False
71
+ if self.process:
72
+ self.process.terminate()
73
+ self.process.wait()
74
+ self.process = None
75
+ print("Mindcraft shut down.")
76
+
77
+ def wait(self):
78
+ """Block the main thread until Ctrl+C is pressed so the server stays up,"""
79
+ print("Server is running. Press Ctrl+C to exit.")
80
+ try:
81
+ while True:
82
+ time.sleep(1)
83
+ except KeyboardInterrupt:
84
+ print("\nCtrl+C detected. Exiting...")
85
+ self.shutdown()
86
+
87
+ mindcraft_instance = Mindcraft()
88
+
89
+ def init(port=8080):
90
+ mindcraft_instance.init(port)
91
+
92
+ def create_agent(settings_json):
93
+ mindcraft_instance.create_agent(settings_json)
94
+
95
+ def shutdown():
96
+ mindcraft_instance.shutdown()
97
+
98
+ def wait():
99
+ mindcraft_instance.wait()
@@ -0,0 +1,89 @@
1
+ import { promises as fs } from 'fs';
2
+ import path from 'path';
3
+ import { fileURLToPath, pathToFileURL } from 'url';
4
+
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = path.dirname(__filename);
7
+
8
+ // Dynamically discover model classes in this directory.
9
+ // Each model class must export a static `prefix` string.
10
+ const apiMap = await (async () => {
11
+ const map = {};
12
+ const files = (await fs.readdir(__dirname))
13
+ .filter(f => f.endsWith('.js') && f !== '_model_map.js' && f !== 'prompter.js');
14
+ for (const file of files) {
15
+ try {
16
+ const moduleUrl = pathToFileURL(path.join(__dirname, file)).href;
17
+ const mod = await import(moduleUrl);
18
+ for (const exported of Object.values(mod)) {
19
+ if (typeof exported === 'function' && Object.prototype.hasOwnProperty.call(exported, 'prefix')) {
20
+ const prefix = exported.prefix;
21
+ if (typeof prefix === 'string' && prefix.length > 0) {
22
+ map[prefix] = exported;
23
+ }
24
+ }
25
+ }
26
+ } catch (e) {
27
+ console.warn('Failed to load model module:', file, e?.message || e);
28
+ }
29
+ }
30
+ return map;
31
+ })();
32
+
33
+ export function selectAPI(profile) {
34
+ if (typeof profile === 'string' || profile instanceof String) {
35
+ profile = {model: profile};
36
+ }
37
+ // backwards compatibility with local->ollama
38
+ if (profile.api?.includes('local') || profile.model?.includes('local')) {
39
+ profile.api = 'ollama';
40
+ if (profile.model) {
41
+ profile.model = profile.model.replace('local', 'ollama');
42
+ }
43
+ }
44
+ if (!profile.api) {
45
+ const api = Object.keys(apiMap).find(key => profile.model?.startsWith(key));
46
+ if (api) {
47
+ profile.api = api;
48
+ }
49
+ else {
50
+ // check for some common models that do not require prefixes
51
+ if (profile.model.includes('gpt') || profile.model.includes('o1')|| profile.model.includes('o3'))
52
+ profile.api = 'openai';
53
+ else if (profile.model.includes('claude'))
54
+ profile.api = 'anthropic';
55
+ else if (profile.model.includes('gemini'))
56
+ profile.api = "google";
57
+ else if (profile.model.includes('grok'))
58
+ profile.api = 'xai';
59
+ else if (profile.model.includes('mistral'))
60
+ profile.api = 'mistral';
61
+ else if (profile.model.includes('deepseek'))
62
+ profile.api = 'deepseek';
63
+ else if (profile.model.includes('qwen'))
64
+ profile.api = 'qwen';
65
+ }
66
+ if (!profile.api) {
67
+ throw new Error('Unknown model:', profile.model);
68
+ }
69
+ }
70
+ if (!apiMap[profile.api]) {
71
+ throw new Error('Unknown api:', profile.api);
72
+ }
73
+ let model_name = profile.model.replace(profile.api + '/', ''); // remove prefix
74
+ profile.model = model_name === "" ? null : model_name; // if model is empty, set to null
75
+ return profile;
76
+ }
77
+
78
+ export function createModel(profile) {
79
+ if (!!apiMap[profile.model]) {
80
+ // if the model value is an api (instead of a specific model name)
81
+ // then set model to null so it uses the default model for that api
82
+ profile.model = null;
83
+ }
84
+ if (!apiMap[profile.api]) {
85
+ throw new Error('Unknown api:', profile.api);
86
+ }
87
+ const model = new apiMap[profile.api](profile.model, profile.url, profile.params);
88
+ return model;
89
+ }
@@ -0,0 +1,32 @@
1
+ import { AzureOpenAI } from "openai";
2
+ import { getKey, hasKey } from '../utils/keys.js';
3
+ import { GPT } from './gpt.js'
4
+
5
+ export class AzureGPT extends GPT {
6
+ static prefix = 'azure';
7
+ constructor(model_name, url, params) {
8
+ super(model_name, url)
9
+
10
+ this.model_name = model_name;
11
+ this.params = params || {};
12
+
13
+ const config = {};
14
+
15
+ if (url)
16
+ config.endpoint = url;
17
+
18
+ config.apiKey = hasKey('AZURE_OPENAI_API_KEY') ? getKey('AZURE_OPENAI_API_KEY') : getKey('OPENAI_API_KEY');
19
+
20
+ config.deployment = model_name;
21
+
22
+ if (this.params.apiVersion) {
23
+ config.apiVersion = this.params.apiVersion;
24
+ delete this.params.apiVersion; // remove from params for later use in requests
25
+ }
26
+ else {
27
+ throw new Error('apiVersion is required in params for azure!');
28
+ }
29
+
30
+ this.openai = new AzureOpenAI(config)
31
+ }
32
+ }
@@ -0,0 +1,61 @@
1
+ import CerebrasSDK from '@cerebras/cerebras_cloud_sdk';
2
+ import { strictFormat } from '../utils/text.js';
3
+ import { getKey } from '../utils/keys.js';
4
+
5
+ export class Cerebras {
6
+ static prefix = 'cerebras';
7
+ constructor(model_name, url, params) {
8
+ this.model_name = model_name;
9
+ this.url = url;
10
+ this.params = params;
11
+
12
+ // Initialize client with API key
13
+ this.client = new CerebrasSDK({ apiKey: getKey('CEREBRAS_API_KEY') });
14
+ }
15
+
16
+ async sendRequest(turns, systemMessage, stop_seq = '***') {
17
+ // Format messages array
18
+ const messages = strictFormat(turns);
19
+ messages.unshift({ role: 'system', content: systemMessage });
20
+
21
+ const pack = {
22
+ model: this.model_name || 'gpt-oss-120b',
23
+ messages,
24
+ stream: false,
25
+ ...(this.params || {}),
26
+ };
27
+
28
+ let res;
29
+ try {
30
+ const completion = await this.client.chat.completions.create(pack);
31
+ // OpenAI-compatible shape
32
+ res = completion.choices?.[0]?.message?.content || '';
33
+ } catch (err) {
34
+ console.error('Cerebras API error:', err);
35
+ res = 'My brain disconnected, try again.';
36
+ }
37
+ return res;
38
+ }
39
+
40
+ async sendVisionRequest(messages, systemMessage, imageBuffer) {
41
+ const imageMessages = [...messages];
42
+ imageMessages.push({
43
+ role: "user",
44
+ content: [
45
+ { type: "text", text: systemMessage },
46
+ {
47
+ type: "image_url",
48
+ image_url: {
49
+ url: `data:image/jpeg;base64,${imageBuffer.toString('base64')}`
50
+ }
51
+ }
52
+ ]
53
+ });
54
+
55
+ return this.sendRequest(imageMessages, systemMessage);
56
+ }
57
+
58
+ async embed(text) {
59
+ throw new Error('Embeddings are not supported by Cerebras.');
60
+ }
61
+ }
@@ -0,0 +1,87 @@
1
+ import Anthropic from '@anthropic-ai/sdk';
2
+ import { strictFormat } from '../utils/text.js';
3
+ import { getKey } from '../utils/keys.js';
4
+
5
+ export class Claude {
6
+ static prefix = 'anthropic';
7
+ constructor(model_name, url, params) {
8
+ this.model_name = model_name;
9
+ this.params = params || {};
10
+
11
+ let config = {};
12
+ if (url)
13
+ config.baseURL = url;
14
+
15
+ config.apiKey = getKey('ANTHROPIC_API_KEY');
16
+
17
+ this.anthropic = new Anthropic(config);
18
+ }
19
+
20
+ async sendRequest(turns, systemMessage) {
21
+ const messages = strictFormat(turns);
22
+ let res = null;
23
+ try {
24
+ console.log(`Awaiting anthropic response from ${this.model_name}...`)
25
+ if (!this.params.max_tokens) {
26
+ if (this.params.thinking?.budget_tokens) {
27
+ this.params.max_tokens = this.params.thinking.budget_tokens + 1000;
28
+ // max_tokens must be greater than thinking.budget_tokens
29
+ } else {
30
+ this.params.max_tokens = 4096;
31
+ }
32
+ }
33
+ const resp = await this.anthropic.messages.create({
34
+ model: this.model_name || "claude-sonnet-4-20250514",
35
+ system: systemMessage,
36
+ messages: messages,
37
+ ...(this.params || {})
38
+ });
39
+
40
+ console.log('Received.')
41
+ // get first content of type text
42
+ const textContent = resp.content.find(content => content.type === 'text');
43
+ if (textContent) {
44
+ res = textContent.text;
45
+ } else {
46
+ console.warn('No text content found in the response.');
47
+ res = 'No response from Claude.';
48
+ }
49
+ }
50
+ catch (err) {
51
+ if (err.message.includes("does not support image input")) {
52
+ res = "Vision is only supported by certain models.";
53
+ } else {
54
+ res = "My brain disconnected, try again.";
55
+ }
56
+ console.log(err);
57
+ }
58
+ return res;
59
+ }
60
+
61
+ async sendVisionRequest(turns, systemMessage, imageBuffer) {
62
+ const imageMessages = [...turns];
63
+ imageMessages.push({
64
+ role: "user",
65
+ content: [
66
+ {
67
+ type: "text",
68
+ text: systemMessage
69
+ },
70
+ {
71
+ type: "image",
72
+ source: {
73
+ type: "base64",
74
+ media_type: "image/jpeg",
75
+ data: imageBuffer.toString('base64')
76
+ }
77
+ }
78
+ ]
79
+ });
80
+
81
+ return this.sendRequest(imageMessages, systemMessage);
82
+ }
83
+
84
+ async embed(text) {
85
+ throw new Error('Embeddings are not supported by Claude.');
86
+ }
87
+ }