Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add feedback and feedback_task with cli #102

Merged
merged 12 commits into from
Feb 13, 2024
51 changes: 51 additions & 0 deletions examples/feedback/simple_feedback.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import uuid
from typing import Literal

from pydantic import BaseModel, Field

from log10.feedback.feedback import Feedback
from log10.feedback.feedback_task import FeedbackTask
from log10.load import OpenAI


#
# use log10 to log an openai completion
#

# create a unique id
unique_id = str(uuid.uuid4())
print(f"Use tag: {unique_id}")
client = OpenAI(tags=[unique_id])
completion = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[
{
"role": "system",
"content": "You are the most knowledgable Star Wars guru on the planet",
},
{
"role": "user",
"content": "Write the time period of all the Star Wars movies and spinoffs?",
},
],
)
print(completion.choices[0].message)

#
# add feedback to the completion
#


# define a feedback task
class EmojiFeedback(BaseModel):
feedback: Literal["😀", "🙁"] = Field(..., description="User feedback with emojis")


# create a feedback
fb = EmojiFeedback(feedback="😀")

task = FeedbackTask().create(name="emoji_task_test", task_schema=fb.model_json_schema())
task_dump = task.json()

print(fb.model_dump_json())
Feedback().create(task_id=task_dump["id"], values=fb.model_dump(), completion_tags_selector=[unique_id])
28 changes: 28 additions & 0 deletions log10/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import click

from log10.feedback.feedback import create_feedback
from log10.feedback.feedback_task import create_feedback_task


@click.group()
def cli():
pass


@click.group()
def feedback():
pass


@click.group()
def feedback_task():
pass


cli.add_command(feedback)
feedback.add_command(create_feedback, "create")
cli.add_command(feedback_task)
feedback_task.add_command(create_feedback_task, "create")

if __name__ == "__main__":
cli()
65 changes: 65 additions & 0 deletions log10/feedback/feedback.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import json
import logging

import click
import httpx
from dotenv import load_dotenv

from log10.llm import Log10Config


load_dotenv()

logging.basicConfig(
format="[%(asctime)s - %(name)s - %(levelname)s] %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)
logger: logging.Logger = logging.getLogger("LOG10")
logger.setLevel(logging.INFO)


class Feedback:
feedback_create_url = "api/v1/feedback"

def __init__(self, log10_config: Log10Config = None):
self._log10_config = log10_config or Log10Config()
self._http_client = httpx.Client()

def _post_request(self, url: str, json_payload: dict) -> httpx.Response:
headers = {
"x-log10-token": self._log10_config.token,
"x-log10-organization": self._log10_config.org_id,
wenzhe-log10 marked this conversation as resolved.
Show resolved Hide resolved
"Content-Type": "application/json",
}
json_payload["organization_id"] = self._log10_config.org_id
try:
res = self._http_client.post(self._log10_config.url + url, headers=headers, json=json_payload)
res.raise_for_status()
return res
except Exception as e:
logger.error(e)
logger.error(e.response.json()["error"])
raise

def create(
self, task_id: str, values: dict, completion_tags_selector: list[str], comment: str = None
) -> httpx.Response:
json_payload = {
"task_id": task_id,
"json_values": values,
"completion_tags_selector": completion_tags_selector,
}
res = self._post_request(self.feedback_create_url, json_payload)
return res


@click.command()
@click.option("--task_id", prompt="Enter task id", help="Task ID")
@click.option("--values", prompt="Enter task values", help="Feedback in JSON format")
@click.option("--completion_tags_selector", prompt="Enter completion tags selector", help="Completion tags selector")
def create_feedback(task_id, values, completion_tags_selector):
click.echo("Creating feedback")
tags = completion_tags_selector.split(",")
values = json.loads(values)
feedback = Feedback().create(task_id=task_id, values=values, completion_tags_selector=tags)
click.echo(feedback.json())
63 changes: 63 additions & 0 deletions log10/feedback/feedback_task.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import json
import logging

import click
import httpx
from dotenv import load_dotenv

from log10.llm import Log10Config


load_dotenv()

logging.basicConfig(
format="[%(asctime)s - %(name)s - %(levelname)s] %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)
logger: logging.Logger = logging.getLogger("LOG10")
logger.setLevel(logging.INFO)


class FeedbackTask:
feedback_task_create_url = "api/v1/feedback_task"

def __init__(self, log10_config: Log10Config = None):
self._log10_config = log10_config or Log10Config()
self._http_client = httpx.Client()

def _post_request(self, url: str, json_payload: dict) -> httpx.Response:
headers = {
"x-log10-token": self._log10_config.token,
"Content-Type": "application/json",
"x-log10-organization": self._log10_config.org_id,
wenzhe-log10 marked this conversation as resolved.
Show resolved Hide resolved
}
json_payload["organization_id"] = self._log10_config.org_id
try:
res = self._http_client.post(self._log10_config.url + url, headers=headers, json=json_payload)
res.raise_for_status()
return res
except Exception as e:
logger.error(e)
logger.error(e.response.json()["error"])
raise

def create(self, task_schema: dict, name: str = None, instruction: str = None) -> httpx.Response:
json_payload = {"json_schema": task_schema}
if name:
json_payload["name"] = name
if instruction:
json_payload["instruction"] = instruction

res = self._post_request(self.feedback_task_create_url, json_payload)
return res


# create a cli interface for FeebackTask.create function
@click.command()
@click.option("--name", prompt="Enter feedback task name", help="Name of the task")
@click.option("--task_schema", prompt="Enter feedback task schema", help="Task schema")
def create_feedback_task(name, task_schema):
click.echo("Creating feedback task")
task_schema = json.loads(task_schema)
task = FeedbackTask().create(name=name, task_schema=task_schema)
click.echo(f"Use this task_id to add feedback: {task.json()['id']}")
11 changes: 7 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ packages = [
{ include = "log10" },
]

[tool.poetry.scripts]
log10 = "log10.__main__:cli"

[tool.poetry.group.dev.dependencies]
build = "^0.10.0"
pytest = "^8.0.0"
Expand Down Expand Up @@ -48,17 +51,17 @@ together = "^0.2.7"

[tool.ruff]
# Never enforce `E501` (line length violations).
ignore = ["C901", "E501", "E741", "F402", "F823" ]
select = ["C", "E", "F", "I", "W"]
lint.ignore = ["C901", "E501", "E741", "F402", "F823" ]
lint.select = ["C", "E", "F", "I", "W"]
line-length = 119

# Ignore import violations in all `__init__.py` files.
[tool.ruff.per-file-ignores]
[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["E402", "F401", "F403", "F811"]
"log10/langchain.py" = ["E402"]
"examples/logging/*.py" = ["E402", "F821"]

[tool.ruff.isort]
[tool.ruff.lint.isort]
lines-after-imports = 2
known-first-party = ["log10"]

Expand Down
Loading