diff --git a/package.json b/package.json
index 372d54db50..0d394aa59b 100644
--- a/package.json
+++ b/package.json
@@ -47,7 +47,8 @@
     "dependencies": {
         "ollama-ai-provider": "^0.16.1",
         "optional": "^0.1.4",
-        "sharp": "^0.33.5"
+        "sharp": "^0.33.5",
+        "tslog": "^4.9.3"
     },
     "packageManager": "pnpm@9.12.3+sha512.cce0f9de9c5a7c95bef944169cc5dfe8741abfb145078c0d508b868056848a87c81e626246cb60967cbd7fd29a6c062ef73ff840d96b3c86c40ac92cf4a813ee"
 }
diff --git a/packages/core/src/defaultCharacter.ts b/packages/core/src/defaultCharacter.ts
index 2ca1bf0179..1a1b4e7583 100644
--- a/packages/core/src/defaultCharacter.ts
+++ b/packages/core/src/defaultCharacter.ts
@@ -5,7 +5,7 @@ export const defaultCharacter: Character = {
     username: "eliza",
     plugins: [],
     clients: [],
-    modelProvider: ModelProviderName.OPENAI,
+    modelProvider: ModelProviderName.OLLAMA,
     settings: {
         secrets: {},
         voice: {
diff --git a/packages/core/src/embedding.ts b/packages/core/src/embedding.ts
index 0bb91f136b..83b287fa78 100644
--- a/packages/core/src/embedding.ts
+++ b/packages/core/src/embedding.ts
@@ -86,8 +86,10 @@ export async function embed(runtime: IAgentRuntime, input: string) {
     // 3. Fallback to OpenAI embedding model
     const embeddingModel = settings.USE_OPENAI_EMBEDDING
         ? "text-embedding-3-small"
-        : modelProvider.model?.[ModelClass.EMBEDDING] ||
-          models[ModelProviderName.OPENAI].model[ModelClass.EMBEDDING];
+        : runtime.character.modelProvider === ModelProviderName.OLLAMA
+          ? settings.OLLAMA_EMBEDDING_MODEL || "mxbai-embed-large"
+          : modelProvider.model?.[ModelClass.EMBEDDING] ||
+            models[ModelProviderName.OPENAI].model[ModelClass.EMBEDDING];
 
     if (!embeddingModel) {
         throw new Error("No embedding model configured");
diff --git a/packages/core/src/generation.ts b/packages/core/src/generation.ts
index f20004ea9b..d58a0fdbde 100644
--- a/packages/core/src/generation.ts
+++ b/packages/core/src/generation.ts
@@ -62,7 +62,12 @@ export async function generateText({
         return "";
     }
 
-    elizaLogger.log("Genarating text...");
+    elizaLogger.log("Generating text...");
+
+    elizaLogger.info("Generating text with options:", {
+        modelProvider: runtime.modelProvider,
+        model: modelClass,
+    });
 
     const provider = runtime.modelProvider;
     const endpoint =
@@ -84,6 +89,8 @@ export async function generateText({
         model = runtime.getSetting("LLAMACLOUD_MODEL_SMALL");
     }
 
+    elizaLogger.info("Selected model:", model);
+
     const temperature = models[provider].settings.temperature;
     const frequency_penalty = models[provider].settings.frequency_penalty;
     const presence_penalty = models[provider].settings.presence_penalty;
@@ -709,7 +716,7 @@ export async function generateMessageResponse({
     let retryLength = 1000; // exponential backoff
     while (true) {
         try {
-            elizaLogger.log("Genarating message response..");
+            elizaLogger.log("Generating message response..");
 
             const response = await generateText({
                 runtime,
diff --git a/packages/core/src/logger.ts b/packages/core/src/logger.ts
index f8172d0b6c..ae9b3a1985 100644
--- a/packages/core/src/logger.ts
+++ b/packages/core/src/logger.ts
@@ -1,4 +1,11 @@
-class ElizaLogger {
+import settings from "./settings.ts";
+import { Logger, ILogObjMeta, ILogObj } from "tslog";
+
+interface IElizaLogger extends Logger<IElizaLogger> {
+    progress(message: string): void;
+}
+
+class ElizaLogger implements IElizaLogger {
     constructor() {
         // Check if we're in Node.js environment
         this.isNode =
@@ -7,7 +14,7 @@ class ElizaLogger {
             process.versions.node != null;
 
         // Set verbose based on environment
-        this.verbose = this.isNode ? process.env.verbose === "true" : false;
+        this.verbose = this.isNode ? settings.VERBOSE === "true" : false;
     }
 
     private isNode: boolean;
@@ -173,6 +180,7 @@ class ElizaLogger {
         }
     }
 
+    // @ts-ignore - custom implementation
     log(...strings) {
         this.#logWithStyle(strings, {
             fg: "white",
@@ -182,6 +190,7 @@ class ElizaLogger {
         });
     }
 
+    // @ts-ignore - custom implementation
     warn(...strings) {
         this.#logWithStyle(strings, {
             fg: "yellow",
@@ -191,6 +200,7 @@ class ElizaLogger {
         });
     }
 
+    // @ts-ignore - custom implementation
     error(...strings) {
         this.#logWithStyle(strings, {
             fg: "red",
@@ -200,6 +210,7 @@ class ElizaLogger {
         });
     }
 
+    // @ts-ignore - custom implementation
     info(...strings) {
         this.#logWithStyle(strings, {
             fg: "blue",
@@ -209,15 +220,7 @@ class ElizaLogger {
         });
     }
 
-    success(...strings) {
-        this.#logWithStyle(strings, {
-            fg: "green",
-            bg: "",
-            icon: "\u2713",
-            groupTitle: ` ${this.successesTitle}`,
-        });
-    }
-
+    // @ts-ignore - custom implementation
     debug(...strings) {
         if (!this.verbose) return;
         this.#logWithStyle(strings, {
@@ -228,6 +231,15 @@ class ElizaLogger {
         });
     }
 
+    success(...strings) {
+        this.#logWithStyle(strings, {
+            fg: "green",
+            bg: "",
+            icon: "\u2713",
+            groupTitle: ` ${this.successesTitle}`,
+        });
+    }
+
     assert(...strings) {
         this.#logWithStyle(strings, {
             fg: "cyan",
@@ -236,6 +248,17 @@ class ElizaLogger {
             groupTitle: ` ${this.assertsTitle}`,
         });
     }
+
+    progress(message: string) {
+        if (this.isNode) {
+            // Clear the current line and move cursor to beginning
+            process.stdout.clearLine(0);
+            process.stdout.cursorTo(0);
+            process.stdout.write(message);
+        } else {
+            console.log(message);
+        }
+    }
 }
 
 export const elizaLogger = new ElizaLogger();
diff --git a/packages/core/src/runtime.ts b/packages/core/src/runtime.ts
index f973b321d6..825ca3e2fc 100644
--- a/packages/core/src/runtime.ts
+++ b/packages/core/src/runtime.ts
@@ -176,7 +176,9 @@ export class AgentRuntime implements IAgentRuntime {
             return;
         }
 
+        // Add the service to the services map
         this.services.set(serviceType, service);
+        elizaLogger.success(`Service ${serviceType} registered successfully`);
     }
 
     /**
@@ -217,6 +219,12 @@ export class AgentRuntime implements IAgentRuntime {
         cacheManager: ICacheManager;
         logging?: boolean;
     }) {
+        elizaLogger.info("Initializing AgentRuntime with options:", {
+            character: opts.character?.name,
+            modelProvider: opts.modelProvider,
+            characterModelProvider: opts.character?.modelProvider,
+        });
+
         this.#conversationLength =
             opts.conversationLength ?? this.#conversationLength;
         this.databaseAdapter = opts.databaseAdapter;
@@ -280,10 +288,32 @@ export class AgentRuntime implements IAgentRuntime {
         });
 
         this.serverUrl = opts.serverUrl ?? this.serverUrl;
+
+        elizaLogger.info("Setting model provider...");
+        elizaLogger.info(
+            "- Character model provider:",
+            this.character.modelProvider
+        );
+        elizaLogger.info("- Opts model provider:", opts.modelProvider);
+        elizaLogger.info("- Current model provider:", this.modelProvider);
+
         this.modelProvider =
             this.character.modelProvider ??
             opts.modelProvider ??
             this.modelProvider;
+
+        elizaLogger.info("Selected model provider:", this.modelProvider);
+
+        // Validate model provider
+        if (!Object.values(ModelProviderName).includes(this.modelProvider)) {
+            elizaLogger.error("Invalid model provider:", this.modelProvider);
+            elizaLogger.error(
+                "Available providers:",
+                Object.values(ModelProviderName)
+            );
+            throw new Error(`Invalid model provider: ${this.modelProvider}`);
+        }
+
         if (!this.serverUrl) {
             elizaLogger.warn("No serverUrl provided, defaulting to localhost");
         }
diff --git a/packages/plugin-node/src/services/image.ts b/packages/plugin-node/src/services/image.ts
index 3f60566f66..d840e7f1f4 100644
--- a/packages/plugin-node/src/services/image.ts
+++ b/packages/plugin-node/src/services/image.ts
@@ -63,7 +63,7 @@ export class ImageDescriptionService
         env.backends.onnx.wasm.proxy = false;
         env.backends.onnx.wasm.numThreads = 1;
 
-        elizaLogger.log("Downloading Florence model...");
+        elizaLogger.info("Downloading Florence model...");
 
         this.model = await Florence2ForConditionalGeneration.from_pretrained(
             this.modelId,
@@ -71,8 +71,15 @@ export class ImageDescriptionService
                 device: "gpu",
                 progress_callback: (progress) => {
                     if (progress.status === "downloading") {
-                        elizaLogger.log(
-                            `Model download progress: ${JSON.stringify(progress)}`
+                        const percent = (
+                            (progress.loaded / progress.total) *
+                            100
+                        ).toFixed(1);
+                        const dots = ".".repeat(
+                            Math.floor(Number(percent) / 5)
+                        );
+                        elizaLogger.info(
+                            `Downloading Florence model: [${dots.padEnd(20, " ")}] ${percent}%`
                         );
                     }
                 },
@@ -81,10 +88,14 @@ export class ImageDescriptionService
 
         elizaLogger.success("Florence model downloaded successfully");
 
+        elizaLogger.info("Downloading processor...");
         this.processor = (await AutoProcessor.from_pretrained(
             this.modelId
         )) as Florence2Processor;
+
+        elizaLogger.info("Downloading tokenizer...");
         this.tokenizer = await AutoTokenizer.from_pretrained(this.modelId);
+        elizaLogger.success("Image service initialization complete");
     }
 
     async describeImage(
diff --git a/packages/plugin-node/src/services/llama.ts b/packages/plugin-node/src/services/llama.ts
index 7a747136c0..0a08b3754f 100644
--- a/packages/plugin-node/src/services/llama.ts
+++ b/packages/plugin-node/src/services/llama.ts
@@ -1,4 +1,9 @@
-import { elizaLogger, IAgentRuntime, ServiceType } from "@ai16z/eliza";
+import {
+    elizaLogger,
+    IAgentRuntime,
+    ServiceType,
+    ModelProviderName,
+} from "@ai16z/eliza";
 import { Service } from "@ai16z/eliza";
 import fs from "fs";
 import https from "https";
@@ -164,6 +169,7 @@ export class LlamaService extends Service {
     private ctx: LlamaContext | undefined;
     private sequence: LlamaContextSequence | undefined;
     private modelUrl: string;
+    private ollamaModel: string | undefined;
 
     private messageQueue: QueuedMessage[] = [];
     private isProcessing: boolean = false;
@@ -179,18 +185,46 @@ export class LlamaService extends Service {
             "https://huggingface.co/NousResearch/Hermes-3-Llama-3.1-8B-GGUF/resolve/main/Hermes-3-Llama-3.1-8B.Q8_0.gguf?download=true";
         const modelName = "model.gguf";
         this.modelPath = path.join(__dirname, modelName);
+        this.ollamaModel = process.env.OLLAMA_MODEL;
     }
 
-    async initialize(_runtime: IAgentRuntime): Promise<void> {}
+    async initialize(runtime: IAgentRuntime): Promise<void> {
+        elizaLogger.info("Initializing LlamaService...");
+        try {
+            // Check if we should use Ollama
+            if (runtime.modelProvider === ModelProviderName.OLLAMA) {
+                elizaLogger.info("Using Ollama provider");
+                this.modelInitialized = true;
+                return;
+            }
+
+            elizaLogger.info("Using local GGUF model");
+            elizaLogger.info("Ensuring model is initialized...");
+            await this.ensureInitialized();
+            elizaLogger.success("LlamaService initialized successfully");
+        } catch (error) {
+            elizaLogger.error("Failed to initialize LlamaService:", error);
+            // Re-throw with more context
+            throw new Error(
+                `LlamaService initialization failed: ${error.message}`
+            );
+        }
+    }
 
     private async ensureInitialized() {
         if (!this.modelInitialized) {
+            elizaLogger.info(
+                "Model not initialized, starting initialization..."
+            );
             await this.initializeModel();
+        } else {
+            elizaLogger.info("Model already initialized");
         }
     }
 
     async initializeModel() {
         try {
+            elizaLogger.info("Checking model file...");
             await this.checkModel();
 
             const systemInfo = await si.graphics();
@@ -199,92 +233,150 @@ export class LlamaService extends Service {
             );
 
             if (hasCUDA) {
-                console.log("**** LlamaService: CUDA detected");
+                elizaLogger.info(
+                    "LlamaService: CUDA detected, using GPU acceleration"
+                );
             } else {
-                console.warn(
-                    "**** LlamaService: No CUDA detected - local response will be slow"
+                elizaLogger.warn(
+                    "LlamaService: No CUDA detected - local response will be slow"
                 );
             }
 
+            elizaLogger.info("Initializing Llama instance...");
             this.llama = await getLlama({
-                gpu: "cuda",
+                gpu: hasCUDA ? "cuda" : undefined,
             });
+
+            elizaLogger.info("Creating JSON schema grammar...");
             const grammar = new LlamaJsonSchemaGrammar(
                 this.llama,
                 jsonSchemaGrammar as GbnfJsonSchema
             );
             this.grammar = grammar;
 
+            elizaLogger.info("Loading model...");
             this.model = await this.llama.loadModel({
                 modelPath: this.modelPath,
             });
 
+            elizaLogger.info("Creating context and sequence...");
             this.ctx = await this.model.createContext({ contextSize: 8192 });
             this.sequence = this.ctx.getSequence();
 
             this.modelInitialized = true;
+            elizaLogger.success("Model initialization complete");
             this.processQueue();
         } catch (error) {
-            console.error(
-                "Model initialization failed. Deleting model and retrying...",
+            elizaLogger.error(
+                "Model initialization failed. Deleting model and retrying:",
                 error
             );
-            await this.deleteModel();
-            await this.initializeModel();
+            try {
+                elizaLogger.info(
+                    "Attempting to delete and re-download model..."
+                );
+                await this.deleteModel();
+                await this.initializeModel();
+            } catch (retryError) {
+                elizaLogger.error(
+                    "Model re-initialization failed:",
+                    retryError
+                );
+                throw new Error(
+                    `Model initialization failed after retry: ${retryError.message}`
+                );
+            }
         }
     }
 
     async checkModel() {
         if (!fs.existsSync(this.modelPath)) {
+            elizaLogger.info("Model file not found, starting download...");
             await new Promise<void>((resolve, reject) => {
                 const file = fs.createWriteStream(this.modelPath);
                 let downloadedSize = 0;
+                let totalSize = 0;
 
                 const downloadModel = (url: string) => {
                     https
                         .get(url, (response) => {
-                            const isRedirect =
+                            if (
                                 response.statusCode >= 300 &&
-                                response.statusCode < 400;
-                            if (isRedirect) {
-                                const redirectUrl = response.headers.location;
-                                if (redirectUrl) {
-                                    downloadModel(redirectUrl);
-                                    return;
-                                } else {
-                                    reject(new Error("Redirect URL not found"));
-                                    return;
-                                }
+                                response.statusCode < 400 &&
+                                response.headers.location
+                            ) {
+                                elizaLogger.info(
+                                    `Following redirect to: ${response.headers.location}`
+                                );
+                                downloadModel(response.headers.location);
+                                return;
+                            }
+
+                            if (response.statusCode !== 200) {
+                                reject(
+                                    new Error(
+                                        `Failed to download model: HTTP ${response.statusCode}`
+                                    )
+                                );
+                                return;
                             }
 
-                            const totalSize = parseInt(
-                                response.headers["content-length"] ?? "0",
+                            totalSize = parseInt(
+                                response.headers["content-length"] || "0",
                                 10
                             );
+                            elizaLogger.info(
+                                `Downloading model: Hermes-3-Llama-3.1-8B.Q8_0.gguf`
+                            );
+                            elizaLogger.info(
+                                `Download location: ${this.modelPath}`
+                            );
+                            elizaLogger.info(
+                                `Total size: ${(totalSize / 1024 / 1024).toFixed(2)} MB`
+                            );
+
+                            response.pipe(file);
 
+                            let progressString = "";
                             response.on("data", (chunk) => {
                                 downloadedSize += chunk.length;
-                                file.write(chunk);
-
-                                // Log progress
-                                const progress = (
-                                    (downloadedSize / totalSize) *
-                                    100
-                                ).toFixed(2);
-                                process.stdout.write(
-                                    `Downloaded ${progress}%\r`
+                                const progress =
+                                    totalSize > 0
+                                        ? (
+                                              (downloadedSize / totalSize) *
+                                              100
+                                          ).toFixed(1)
+                                        : "0.0";
+                                const dots = ".".repeat(
+                                    Math.floor(Number(progress) / 5)
                                 );
+                                progressString = `Downloading model: [${dots.padEnd(20, " ")}] ${progress}%`;
+                                elizaLogger.progress(progressString);
                             });
 
-                            response.on("end", () => {
-                                file.end();
+                            file.on("finish", () => {
+                                file.close();
+                                elizaLogger.progress(""); // Clear the progress line
+                                elizaLogger.success("Model download complete");
                                 resolve();
                             });
+
+                            response.on("error", (error) => {
+                                fs.unlink(this.modelPath, () => {});
+                                reject(
+                                    new Error(
+                                        `Model download failed: ${error.message}`
+                                    )
+                                );
+                            });
                         })
-                        .on("error", (err) => {
-                            fs.unlink(this.modelPath, () => {}); // Delete the file async
-                            console.error("Download failed:", err.message);
-                            reject(err);
+                        .on("error", (error) => {
+                            fs.unlink(this.modelPath, () => {});
+                            reject(
+                                new Error(
+                                    `Model download request failed: ${error.message}`
+                                )
+                            );
                         });
                 };
 
@@ -392,6 +484,36 @@ export class LlamaService extends Service {
         this.isProcessing = false;
     }
 
+    async completion(prompt: string, runtime: IAgentRuntime): Promise<string> {
+        try {
+            await this.initialize(runtime);
+
+            if (runtime.modelProvider === ModelProviderName.OLLAMA) {
+                return await this.ollamaCompletion(prompt);
+            }
+
+            return await this.localCompletion(prompt);
+        } catch (error) {
+            elizaLogger.error("Error in completion:", error);
+            throw error;
+        }
+    }
+
+    async embedding(text: string, runtime: IAgentRuntime): Promise<number[]> {
+        try {
+            await this.initialize(runtime);
+
+            if (runtime.modelProvider === ModelProviderName.OLLAMA) {
+                return await this.ollamaEmbedding(text);
+            }
+
+            return await this.localEmbedding(text);
+        } catch (error) {
+            elizaLogger.error("Error in embedding:", error);
+            throw error;
+        }
+    }
+
     private async getCompletionResponse(
         context: string,
         temperature: number,
@@ -401,6 +523,42 @@ export class LlamaService extends Service {
         max_tokens: number,
         useGrammar: boolean
     ): Promise<any | string> {
+        const ollamaModel = process.env.OLLAMA_MODEL;
+        if (ollamaModel) {
+            const ollamaUrl =
+                process.env.OLLAMA_SERVER_URL || "http://localhost:11434";
+            elizaLogger.info(
+                `Using Ollama API at ${ollamaUrl} with model ${ollamaModel}`
+            );
+
+            const response = await fetch(`${ollamaUrl}/api/generate`, {
+                method: "POST",
+                headers: { "Content-Type": "application/json" },
+                body: JSON.stringify({
+                    model: ollamaModel,
+                    prompt: context,
+                    stream: false,
+                    options: {
+                        temperature,
+                        stop,
+                        frequency_penalty,
+                        presence_penalty,
+                        num_predict: max_tokens,
+                    },
+                }),
+            });
+
+            if (!response.ok) {
+                throw new Error(
+                    `Ollama request failed: ${response.statusText}`
+                );
+            }
+
+            const result = await response.json();
+            return useGrammar ? { content: result.response } : result.response;
+        }
+
+        // Use local GGUF model
         if (!this.sequence) {
             throw new Error("Model not initialized.");
         }
@@ -429,7 +587,7 @@ export class LlamaService extends Service {
         })) {
             const current = this.model.detokenize([...responseTokens, token]);
             if ([...stop].some((s) => current.includes(s))) {
-                console.log("Stop sequence found");
+                elizaLogger.info("Stop sequence found");
                 break;
             }
 
@@ -437,12 +595,12 @@ export class LlamaService extends Service {
             process.stdout.write(this.model!.detokenize([token]));
             if (useGrammar) {
                 if (current.replaceAll("\n", "").includes("}```")) {
-                    console.log("JSON block found");
+                    elizaLogger.info("JSON block found");
                     break;
                 }
             }
             if (responseTokens.length > max_tokens) {
-                console.log("Max tokens reached");
+                elizaLogger.info("Max tokens reached");
                 break;
             }
         }
@@ -472,7 +630,7 @@ export class LlamaService extends Service {
                 await this.sequence.clearHistory();
                 return parsedResponse;
             } catch (error) {
-                console.error("Error parsing JSON:", error);
+                elizaLogger.error("Error parsing JSON:", error);
             }
         } else {
             await this.sequence.clearHistory();
@@ -481,13 +639,186 @@ export class LlamaService extends Service {
     }
 
     async getEmbeddingResponse(input: string): Promise<number[] | undefined> {
-        await this.ensureInitialized();
-        if (!this.model) {
-            throw new Error("Model not initialized. Call initialize() first.");
+        const ollamaModel = process.env.OLLAMA_MODEL;
+        if (ollamaModel) {
+            const ollamaUrl =
+                process.env.OLLAMA_SERVER_URL || "http://localhost:11434";
+            const embeddingModel =
+                process.env.OLLAMA_EMBEDDING_MODEL || "mxbai-embed-large";
+            elizaLogger.info(
+                `Using Ollama API for embeddings with model ${embeddingModel} (base: ${ollamaModel})`
+            );
+
+            const response = await fetch(`${ollamaUrl}/api/embeddings`, {
+                method: "POST",
+                headers: { "Content-Type": "application/json" },
+                body: JSON.stringify({
+                    model: embeddingModel,
+                    prompt: input,
+                }),
+            });
+
+            if (!response.ok) {
+                throw new Error(
+                    `Ollama embeddings request failed: ${response.statusText}`
+                );
+            }
+
+            const result = await response.json();
+            return result.embedding;
+        }
+
+        // Use local GGUF model
+        if (!this.sequence) {
+            throw new Error("Sequence not initialized");
+        }
+
+        const ollamaUrl =
+            process.env.OLLAMA_SERVER_URL || "http://localhost:11434";
+        const embeddingModel =
+            process.env.OLLAMA_EMBEDDING_MODEL || "mxbai-embed-large";
+        elizaLogger.info(
+            `Using Ollama API for embeddings with model ${embeddingModel} (base: ${this.ollamaModel})`
+        );
+
+        const response = await fetch(`${ollamaUrl}/api/embeddings`, {
+            method: "POST",
+            headers: {
+                "Content-Type": "application/json",
+            },
+            body: JSON.stringify({
+                input: input,
+                model: embeddingModel,
+            }),
+        });
+
+        if (!response.ok) {
+            throw new Error(`Failed to get embedding: ${response.statusText}`);
+        }
+
+        const embedding = await response.json();
+        return embedding.vector;
+    }
+
+    private async ollamaCompletion(prompt: string): Promise<string> {
+        const ollamaModel = process.env.OLLAMA_MODEL;
+        const ollamaUrl =
+            process.env.OLLAMA_SERVER_URL || "http://localhost:11434";
+        elizaLogger.info(
+            `Using Ollama API at ${ollamaUrl} with model ${ollamaModel}`
+        );
+
+        const response = await fetch(`${ollamaUrl}/api/generate`, {
+            method: "POST",
+            headers: { "Content-Type": "application/json" },
+            body: JSON.stringify({
+                model: ollamaModel,
+                prompt: prompt,
+                stream: false,
+                options: {
+                    temperature: 0.7,
+                    stop: ["\n"],
+                    frequency_penalty: 0.5,
+                    presence_penalty: 0.5,
+                    num_predict: 256,
+                },
+            }),
+        });
+
+        if (!response.ok) {
+            throw new Error(`Ollama request failed: ${response.statusText}`);
+        }
+
+        const result = await response.json();
+        return result.response;
+    }
+
+    private async ollamaEmbedding(text: string): Promise<number[]> {
+        const ollamaModel = process.env.OLLAMA_MODEL;
+        const ollamaUrl =
+            process.env.OLLAMA_SERVER_URL || "http://localhost:11434";
+        const embeddingModel =
+            process.env.OLLAMA_EMBEDDING_MODEL || "mxbai-embed-large";
+        elizaLogger.info(
+            `Using Ollama API for embeddings with model ${embeddingModel} (base: ${ollamaModel})`
+        );
+
+        const response = await fetch(`${ollamaUrl}/api/embeddings`, {
+            method: "POST",
+            headers: { "Content-Type": "application/json" },
+            body: JSON.stringify({
+                model: embeddingModel,
+                prompt: text,
+            }),
+        });
+
+        if (!response.ok) {
+            throw new Error(
+                `Ollama embeddings request failed: ${response.statusText}`
+            );
+        }
+
+        const result = await response.json();
+        return result.embedding;
+    }
+
+    private async localCompletion(prompt: string): Promise<string> {
+        if (!this.sequence) {
+            throw new Error("Sequence not initialized");
+        }
+
+        const tokens = this.model!.tokenize(prompt);
+
+        // tokenize the words to punish
+        const wordsToPunishTokens = wordsToPunish
+            .map((word) => this.model!.tokenize(word))
+            .flat();
+
+        const repeatPenalty: LlamaContextSequenceRepeatPenalty = {
+            punishTokens: () => wordsToPunishTokens,
+            penalty: 1.2,
+            frequencyPenalty: 0.5,
+            presencePenalty: 0.5,
+        };
+
+        const responseTokens: Token[] = [];
+
+        for await (const token of this.sequence.evaluate(tokens, {
+            temperature: 0.7,
+            repeatPenalty: repeatPenalty,
+            yieldEogToken: false,
+        })) {
+            const current = this.model.detokenize([...responseTokens, token]);
+            if (current.includes("\n")) {
+                elizaLogger.info("Stop sequence found");
+                break;
+            }
+
+            responseTokens.push(token);
+            process.stdout.write(this.model!.detokenize([token]));
+            if (responseTokens.length > 256) {
+                elizaLogger.info("Max tokens reached");
+                break;
+            }
+        }
+
+        const response = this.model!.detokenize(responseTokens);
+
+        if (!response) {
+            throw new Error("Response is undefined");
+        }
+
+        await this.sequence.clearHistory();
+        return response;
+    }
+
+    private async localEmbedding(text: string): Promise<number[]> {
+        if (!this.sequence) {
+            throw new Error("Sequence not initialized");
         }
 
         const embeddingContext = await this.model.createEmbeddingContext();
-        const embedding = await embeddingContext.getEmbeddingFor(input);
+        const embedding = await embeddingContext.getEmbeddingFor(text);
         return embedding?.vector ? [...embedding.vector] : undefined;
     }
 }
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index e7cddacf7d..5e7297788e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -20,6 +20,9 @@ importers:
       sharp:
         specifier: ^0.33.5
         version: 0.33.5
+      tslog:
+        specifier: ^4.9.3
+        version: 4.9.3
     devDependencies:
       '@commitlint/cli':
         specifier: ^18.4.4
@@ -13980,6 +13983,10 @@ packages:
   tslib@2.8.0:
     resolution: {integrity: sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==}
 
+  tslog@4.9.3:
+    resolution: {integrity: sha512-oDWuGVONxhVEBtschLf2cs/Jy8i7h1T+CpdkTNWQgdAF7DhRo2G8vMCgILKe7ojdEkLhICWgI1LYSSKaJsRgcw==}
+    engines: {node: '>=16'}
+
   tsup@8.3.5:
     resolution: {integrity: sha512-Tunf6r6m6tnZsG9GYWndg0z8dEV7fD733VBFzFJ5Vcm1FtlXB8xBD/rtrBi2a3YKEV7hHtxiZtW5EAVADoe1pA==}
     engines: {node: '>=18'}
@@ -31192,6 +31199,8 @@ snapshots:
 
   tslib@2.8.0: {}
 
+  tslog@4.9.3: {}
+
   tsup@8.3.5(@swc/core@1.9.2(@swc/helpers@0.5.15))(jiti@2.4.0)(postcss@8.4.49)(typescript@5.6.3)(yaml@2.6.1):
     dependencies:
       bundle-require: 5.0.0(esbuild@0.24.0)