zou-group / textgrad

Automatic ''Differentiation'' via Text -- using large language models to backpropagate textual gradients.
http://textgrad.com/
MIT License
945 stars 67 forks source link

implementing ollama #4

Closed MeDott29 closed 2 weeks ago

MeDott29 commented 2 weeks ago

trying to run the getting started example with an ollama server

import textgrad as tg

tg.set_backward_engine("qwen2:1.5b", override=True)
model=tg.BlackboxLLM("qwen2:1.5b")

question_string = ("If it takes 1 hour to dry 25 shirts under the sun, "
                   "how long will it take to dry 30 shirts under the sun? "
                   "Reason step by step")

question = tg.Variable(question_string, 
                       role_description="question to the LLM", 
                       requires_grad=False)

answer = model(question_string)

print(question)

gives the following error

    response_text = self.engine(input_variable.value, system_prompt=system_prompt_value)
                                ^^^^^^^^^^^^^^^^^^^^
AttributeError: 'str' object has no attribute 'value'

image

I tried adding an ollama.py file to textgrad/engine. not sure what else to try.

try:
    from ollama import Client
    # from openai import OpenAI
except ImportError:
    raise ImportError("If you'd like to use OpenAI models, please install the openai package by running `pip install openai`, and add 'OPENAI_API_KEY' to your environment variables.")

import os
import platformdirs
from tenacity import (
    retry,
    stop_after_attempt,
    wait_random_exponential,
)
import json
from .base import EngineLM, CachedEngine
from openai._types import NotGiven

class ChatOllama(EngineLM, CachedEngine):
    DEFAULT_SYSTEM_PROMPT = "You are a helpful, creative, and smart assistant."

    def __init__(
        self,
        model_string="qwen2:0.5b",
        system_prompt=DEFAULT_SYSTEM_PROMPT,
        **kwargs):
        """
        :param model_string:
        :param system_prompt:
        """
        root = platformdirs.user_cache_dir("textgrad")
        cache_path = os.path.join(root, f"cache_ollama_{model_string}.db")

        super().__init__(cache_path=cache_path)

        self.system_prompt = system_prompt
        # if os.getenv("OPENAI_API_KEY") is None:
        #     raise ValueError("Please set the OPENAI_API_KEY environment variable if you'd like to use OpenAI models.")

        self.client = Client(
            # api_key="ollama",
            host="http://localhost:11434",
        )
        self.model_string = model_string

    def generate(
        self, prompt, system_prompt=None, temperature=0, max_tokens=2000, top_p=0.99
    ):

        sys_prompt_arg = system_prompt if system_prompt else self.system_prompt

        cache_or_none = self._check_cache(sys_prompt_arg + prompt)
        if cache_or_none is not None:
            return cache_or_none

        response = self.client.chat(
            model=self.model_string,
            messages=[
                {"role": "system", "content": sys_prompt_arg},
                {"role": "user", "content": prompt},
            ],
            frequency_penalty=0,
            presence_penalty=0,
            stop=None,
            temperature=temperature,
            max_tokens=max_tokens,
            top_p=top_p,
        )

        response = response['message']['content']
        self._save_cache(sys_prompt_arg + prompt, response)
        return response

    def generate_with_messages(self, messages, temperature=0, max_tokens=2000, top_p=0.99):
        prompt = json.dumps(messages)

        cache_or_none = self._check_cache(prompt)
        if cache_or_none is not None:
            return cache_or_none

        response = self.client.chat(
            model=self.model_string,
            messages=messages,
            frequency_penalty=0,
            presence_penalty=0,
            stop=None,
            temperature=temperature,
            max_tokens=max_tokens,
            top_p=top_p,
        )

        response = response['message']['content']
        self._save_cache(prompt, response)
        return response

    @retry(wait=wait_random_exponential(min=1, max=5), stop=stop_after_attempt(5))
    def __call__(self, prompt, **kwargs):
        return self.generate(prompt, **kwargs)
mertyg commented 2 weeks ago

Thanks for doing this!!! Sorry this was an error on our part in README.

In your script, if instead of passing the question string

answer = model(question_string)

you do

answer = model(question)

you should not get this error, I believe. Could you try that?