-
Notifications
You must be signed in to change notification settings - Fork 55
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #652 from astroseger/simple_python_import
Simple python import
- Loading branch information
Showing
5 changed files
with
200 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
!(import! &self simple_import) | ||
|
||
!(import_from example_01 import simple_fun) | ||
!(import_from example_01 import SimpleObject) | ||
|
||
!(bind! so (SimpleObject)) | ||
|
||
; it is important that obj will have type SimpleObject when passed to simple_fun! | ||
!(simple_fun 1 2 "3" (kwarg1 2) (obj so) ) | ||
|
||
!(call_dot so method "arg1" "arg2" (arg3 3)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
def print_args(*args, **kwargs): | ||
print("arguments:") | ||
for a in args: | ||
print(a, type(a)) | ||
print("keyword arguments:") | ||
for k,v in kwargs.items(): | ||
print(k, v, type(v)) | ||
|
||
def simple_fun(*args, **kwargs): | ||
print("Call simple function") | ||
print_args(*args, **kwargs) | ||
print("") | ||
return 0 | ||
|
||
class SimpleObject: | ||
def method(self, *args, **kwargs): | ||
print("Call Method of simple Object") | ||
print_args(*args, **kwargs) | ||
print("") | ||
return "0" | ||
def __str__(self): | ||
return "simple_object" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
!(import! &self simple_import) | ||
|
||
!(import_as numpy as np) | ||
|
||
!(bind! a1 (call_dot np array (ptuple 1 2 3) )) | ||
!(bind! a2 (call_dot a1 __mul__ 3)) | ||
!(bind! a3 (call_dot a1 __add__ a2)) | ||
|
||
|
||
!(__unwrap a1) | ||
!(__unwrap a2) | ||
!(__unwrap a3) | ||
|
||
!(bind! m1 (call_dot np array (ptuple (1 2 3) (4 4 5) (6 7 8)) )) | ||
!(import_as numpy.linalg as linalg) | ||
!(bind! m1_inv (call_dot linalg inv m1)) | ||
|
||
!(__unwrap (call_dot np matmul m1 m1_inv)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
!(import! &self simple_import) | ||
|
||
!(import_from langchain_openai import ChatOpenAI) | ||
!(import_from langchain_core.prompts import ChatPromptTemplate) | ||
!(import_from langchain_core.output_parsers import StrOutputParser) | ||
|
||
|
||
!(bind! model (ChatOpenAI (temperature 0) (model "gpt-3.5-turbo"))) | ||
|
||
!(bind! prompt (call_dot ChatPromptTemplate from_template "tell me a joke about cat")) | ||
|
||
!(bind! chain1 (chain prompt model (StrOutputParser) )) | ||
|
||
!(__unwrap(call_dot chain1 invoke (pdict))) | ||
|
||
!(bind! prompt2 (call_dot ChatPromptTemplate from_messages (ptuple ("system" "You are very funny") ("user" "tell me joke about {foo}")))) | ||
|
||
!(bind! chain2 (chain prompt2 model (StrOutputParser) )) | ||
|
||
!(__unwrap(call_dot chain2 invoke (pdict (foo "dogs") ))) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
from hyperon.atoms import OperationAtom, OperationObject, GroundedAtom, ValueAtom, ExpressionAtom, SymbolAtom, ValueObject | ||
from hyperon.ext import register_atoms | ||
import os | ||
import sys | ||
|
||
def groundedatom_to_python_object(a): | ||
obj = a.get_object() | ||
if isinstance(obj, ValueObject): | ||
obj = obj.value | ||
if isinstance(obj, OperationObject): | ||
obj = obj.content | ||
# At this point it is already python object | ||
if isinstance(obj, PythonCaller): | ||
obj = obj.obj | ||
return obj | ||
|
||
def tuple_to_keyvalue(a): | ||
ac = a.get_children() | ||
if len(ac) != 2: | ||
raise Exception("Syntax error in tuple_to_keyvalue") | ||
return str(ac[0]), groundedatom_to_python_object(ac[1]) | ||
|
||
def atoms_to_args(*atoms): | ||
args = [] | ||
kwargs = {} | ||
for a in atoms: | ||
if isinstance(a, GroundedAtom): | ||
args.append(groundedatom_to_python_object(a)) | ||
elif isinstance(a, ExpressionAtom): | ||
k,v = tuple_to_keyvalue(a) | ||
kwargs[k] = v | ||
else: | ||
raise Exception(f"Unexpected error: {a}, {type(a)}") | ||
return args, kwargs | ||
|
||
class PythonCaller: | ||
def __init__(self, obj): | ||
self.obj = obj | ||
|
||
def __call__(self, *atoms): | ||
args, kwargs = atoms_to_args(*atoms) | ||
return [ValueAtom(PythonCaller(self.obj(*args, **kwargs)))] | ||
|
||
def _import_and_create_operationatom(metta, import_str, obj): | ||
|
||
# we only need these 3 lines to import from the current directory | ||
# TODO fix it somehow differently | ||
current_directory = os.getcwd() | ||
if current_directory not in sys.path: | ||
sys.path.append(current_directory) | ||
|
||
local_scope = {} | ||
exec(import_str, {}, local_scope) | ||
oatom = OperationAtom(obj, PythonCaller(local_scope[obj]), unwrap = False) | ||
metta.register_atom(obj, oatom) | ||
|
||
|
||
def import_from(metta, lib, i, obj): | ||
if str(i) != "import": | ||
raise Exception("bad import syntax") | ||
lib = str(lib) | ||
obj = str(obj) | ||
_import_and_create_operationatom(metta, f"from {lib} import {obj}", obj) | ||
return [] | ||
|
||
def import_as(metta, lib, a, obj): | ||
if str(a) != "as": | ||
raise Exception("bad import syntax") | ||
lib = str(lib) | ||
obj = str(obj) | ||
_import_and_create_operationatom(metta, f"import {lib} as {obj}", obj) | ||
return [] | ||
|
||
def call_with_dot(*atoms): | ||
if len(atoms) < 2: | ||
raise Exception("Syntax error") | ||
obj = groundedatom_to_python_object(atoms[0]) | ||
method = str(atoms[1]) | ||
atoms = atoms[2:] | ||
args, kwargs = atoms_to_args(*atoms) | ||
rez = getattr(obj, method)(*args, **kwargs) | ||
return [ValueAtom(PythonCaller(rez))] | ||
|
||
def __unwrap(obj): | ||
return obj.obj | ||
|
||
@register_atoms(pass_metta=True) | ||
def my_atoms(metta): | ||
return {'import_from': OperationAtom('import_from', lambda *args: import_from(metta, *args), unwrap = False), | ||
'import_as': OperationAtom('import_as', lambda *args: import_as (metta, *args), unwrap = False)} | ||
|
||
@register_atoms() | ||
def my_atoms2(): | ||
return {'__unwrap': OperationAtom('__unwrap', __unwrap), | ||
"call_dot": OperationAtom("call_dot", call_with_dot, unwrap = False)} | ||
|
||
# The functions which are not required for import, but nice for examples | ||
|
||
# convert nested tuples to nested python tuples | ||
def _ptuple(*atoms): | ||
rez = [] | ||
for a in atoms: | ||
if isinstance(a, GroundedAtom): | ||
rez.append(groundedatom_to_python_object(a)) | ||
elif isinstance(a, ExpressionAtom): | ||
rez.append(_ptuple(*a.get_children())) | ||
return tuple(rez) | ||
|
||
def ptuple(*atoms): | ||
return [ValueAtom(_ptuple(*atoms))] | ||
|
||
# convert pair of tuples to python dictionary | ||
def pdict(*atoms): | ||
return [ValueAtom(dict([tuple_to_keyvalue(a) for a in atoms]))] | ||
|
||
# chain python objects with | (syntactic sugar for langchain) | ||
def chain(*atoms): | ||
objects = [groundedatom_to_python_object(a) for a in atoms] | ||
result = objects[0] | ||
for obj in objects[1:]: | ||
result = result | obj | ||
return [ValueAtom(PythonCaller(result))] | ||
|
||
@register_atoms() | ||
def my_atoms3(): | ||
return {"ptuple": OperationAtom("ptuple", ptuple, unwrap = False), | ||
"pdict": OperationAtom("pdict", pdict, unwrap = False), | ||
"chain": OperationAtom("chain", chain, unwrap = False)} |