Skip to content

Commit

Permalink
Merge pull request #820 from martindurant/llm
Browse files Browse the repository at this point in the history
Add experimental AI stuff
  • Loading branch information
martindurant committed Jun 5, 2024
2 parents 29c8878 + ac1fe1b commit b04e271
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 12 deletions.
23 changes: 12 additions & 11 deletions intake/readers/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -585,19 +585,20 @@ def conversions_graph(avoid=None):
nodes = set(
cls.output_instance
for cls in subclasses(readers.BaseReader)
if cls.output_instance and not any(re.findall(_, cls.qname()) for _ in avoid)
if cls.output_instance
and not any(re.findall(_.lower(), cls.qname().lower()) for _ in avoid)
)
graph.add_nodes_from(nodes)

for cls in subclasses(readers.BaseReader):
if any(re.findall(_, cls.qname()) for _ in avoid):
if any(re.findall(_.lower(), cls.qname().lower()) for _ in avoid):
continue
if cls.output_instance:
for impl in cls.implements:
graph.add_node(cls.output_instance)
graph.add_edge(impl.qname(), cls.output_instance, label=cls.qname())
for cls in subclasses(BaseConverter):
if any(re.findall(_, cls.qname()) for _ in avoid):
if any(re.findall(_.lower(), cls.qname().lower()) for _ in avoid):
continue
for inttype, outtype in cls.instances.items():
if inttype != ".*" and inttype != outtype:
Expand Down Expand Up @@ -638,23 +639,22 @@ def path(

g = conversions_graph(avoid=avoid)
alltypes = list(g)
start = start.lower()
matchtypes = [_ for _ in alltypes if re.findall(start, _.lower())]
matchtypes = [_ for _ in alltypes if re.findall(start, _)]
if not matchtypes:
raise ValueError("type found no match: %s", start)
start = matchtypes[0]
if isinstance(end, str):
end = (end,)
matchtypes = [_ for _ in alltypes if any(re.findall(e.lower(), _.lower()) for e in end)]
matchtypes = [_ for _ in alltypes if any(re.findall(e, _) for e in end)]
if not matchtypes:
raise ValueError("outtype found no match: %s", end)
end = matchtypes[0].lower()
end = matchtypes[0]
return sorted(nx.all_simple_edge_paths(g, start, end, cutoff=cutoff), key=len)


def auto_pipeline(
url: str | BaseData,
outtype: str | tuple[str],
outtype: str | tuple[str] = "",
storage_options: dict | None = None,
avoid: list[str] | None = None,
) -> Pipeline:
Expand Down Expand Up @@ -685,9 +685,10 @@ def auto_pipeline(
if isinstance(data, BaseData):
start = data.qname()
steps = path(start, outtype, avoid=avoid)
reader = data.to_reader(outtype=steps[0][0][1])
for s in steps[0][1:]:
reader = reader.transform[s[1]]
reader = data.to_reader(outtype=steps[0][0][1] if steps else outtype)
if steps:
for s in steps[0][1:]:
reader = reader.transform[s[1]]
elif isinstance(data, BaseReader):
reader = data
steps = path(data.output_instance, outtype, avoid=avoid)
Expand Down
67 changes: 66 additions & 1 deletion intake/readers/datatypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,38 @@ def __init__(
self.end_time = end_time


class LlamaCPPService(Service):
"""Simple local HTTP chat
Also had OpenAI compatible endpoints
https://github.com/ggerganov/llama.cpp/blob/master/examples/server/README.md
"""

def open(self):
"""Open chat config and chat page"""
import webbrowser

webbrowser.open(self.url)


class OpenAIService(Service):
"""OpenAI compatible chatbot
See https://platform.openai.com/docs/api-reference/making-requests
"""

def __init__(
self,
url="https://api.openai.com/",
key: str = "sk-no-key-required",
options=None,
metadata=None,
):
self.key = key
super().__init__(url, options=options, metadata=metadata)


class SQLite(FileData):
"""Database data stored in files"""

Expand Down Expand Up @@ -667,14 +699,47 @@ class KerasModel(FileData):
filepattern = "pb$" # possibly protobuf


class GGUF(FileData):
"""Trained model
(see https://github.com/ggerganov/ggml/blob/master/docs/gguf.md)"""

structure = {"model"}
filepattern = "gguf$"
magic = {b"GGUF"}


class SafeTensors(FileData):
"""Trained model
(see https://github.com/huggingface/safetensors?tab=readme-ov-file#format)
"""

# TODO: .bin sees to be an older pytorch-specific version of this
structure = {"model"}
filepattern = "safetensors$"
magic = {(8, b"{")}


class PickleFile(FileData):
"""Python pickle, arbitrary serialized object"""

structure = set()


class ModelConfig(FileData):
"""HuggingFace-style multi-file model directory
Looks like a catalog of related models
"""

structure = {"model"}
filepattern = "config.json"
magic = {b'"model_type":'}


class SKLearnPickleModel(PickleFile):
"""Serialized model made by sklearn"""
"""Trained model made by sklearn and saved as pickle"""


comp_magic = {
Expand Down
85 changes: 85 additions & 0 deletions intake/readers/readers.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
import re

import fsspec
import requests

import intake.readers.datatypes
from intake import import_name, logger
from intake.readers import datatypes
from intake.readers.mixins import PipelineMixin
Expand Down Expand Up @@ -498,6 +500,89 @@ def _read(self, name, **kw):
return loader()


class LlamaServerReader(BaseReader):
"""Create llama.cpp server using local pretrained model file"""

output_instance = "intake.readers.datatypes:ChatService"
implements = {datatypes.GGUF, datatypes.SafeTensors}
imports = {"transformers"}

def _read(self, data, log_file="out.log", **kwargs):
# TODO: list common options, like PORT
import subprocess
import atexit

f = open(log_file, "wb")
cmd = ["server", "-m", data.url]
for k, v in kwargs.items():
if not k.startswith("-"):
k = f"-{k}"
if v not in [None, ""]:
cmd.extend([str(k), str(v)])
else:
cmd.append(str(k))
P = subprocess.Popen(cmd, stdout=f, stderr=f)
with open(log_file, "rb") as f:
while True:
text = f.readline()
if b"http://" in text:
URL = text.rsplit()[-1].decode()
break
if P.poll() is not None:
raise RuntimeError
# TODO: could check {URL}/health
atexit.register(P.terminate)
return intake.readers.datatypes.LlamaCPPService(url=URL, options={"Process": P})


class LlamaCPPCompletion(BaseReader):
implements = {datatypes.LlamaCPPService}
imports = {"requests"}
output_instance = "builtins:str"

def _read(self, data, prompt: str = "", *args, **kwargs):
r = requests.post(
f"{data.url}/completion",
json={"prompt": prompt, **kwargs},
headers={"Content-Type": "application/json"},
)
return r.json()["content"]


class LlamaCPPEmbedding(BaseReader):
implements = {datatypes.LlamaCPPService}
imports = {"requests"}
output_instance = "builtins:str"

def _read(self, data, prompt: str = "", *args, **kwargs):
r = requests.post(
f"{data.url}/embedding",
json={"content": prompt, **kwargs},
headers={"Content-Type": "application/json"},
)
return r.json()["embedding"]


class OpenAICompletion(BaseReader):
implements = {datatypes.OpenAIService}
imports = {"requests"}
output_instance = "builtins:dict"
# related high-volume endpoints, assistant, embeddings

def _read(self, data, messages: list[dict], *args, model="gtp-3.5-turbo", **kwargs):
import requests

url = f"{data.url}/v1/chat/completions"
options = data.options.copy()
options.update(kwargs)
r = requests.get(
url,
headers={"Content-Type": "application/json", "Authorization": f"Bearer {data.key}"},
json=dict(messages=messages, **options),
)
return r.json()["choices"][0]["message"]


class TorchDataset(BaseReader):
output_instance = "torch.utils.data:Dataset"

Expand Down

0 comments on commit b04e271

Please sign in to comment.