emilia 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- emilia-0.1.0/PKG-INFO +7 -0
- emilia-0.1.0/pyproject.toml +22 -0
- emilia-0.1.0/src/emilia/__init__.py +0 -0
- emilia-0.1.0/src/emilia/emilia.py +89 -0
- emilia-0.1.0/src/emilia/hardcoded.py +18 -0
- emilia-0.1.0/src/emilia/main.py +11 -0
- emilia-0.1.0/src/emilia/tools.py +87 -0
- emilia-0.1.0/tests/tests.py +3 -0
emilia-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = [
|
|
3
|
+
"hatchling",
|
|
4
|
+
]
|
|
5
|
+
build-backend = "hatchling.build"
|
|
6
|
+
|
|
7
|
+
[project]
|
|
8
|
+
name = "emilia"
|
|
9
|
+
version = "0.1.0"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"ollama",
|
|
12
|
+
"ddgs",
|
|
13
|
+
"kopilogs",
|
|
14
|
+
"trafilatura",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[project.scripts]
|
|
18
|
+
emilia = "emilia.main:main"
|
|
19
|
+
|
|
20
|
+
[tool.kuvy.run-paths]
|
|
21
|
+
main = "src/emilia/main.py"
|
|
22
|
+
tests = "tests/tests.py"
|
|
File without changes
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
from ollama import (
|
|
2
|
+
chat,
|
|
3
|
+
Message,
|
|
4
|
+
ChatResponse,
|
|
5
|
+
)
|
|
6
|
+
from kopilogs.paint import paint
|
|
7
|
+
|
|
8
|
+
from typing import Sequence
|
|
9
|
+
|
|
10
|
+
from emilia.tools import (
|
|
11
|
+
search_internet,
|
|
12
|
+
what_time_is_it,
|
|
13
|
+
testing,
|
|
14
|
+
)
|
|
15
|
+
from emilia.hardcoded import context
|
|
16
|
+
|
|
17
|
+
class Emilia:
|
|
18
|
+
def __init__(self, model: str):
|
|
19
|
+
self.model = model
|
|
20
|
+
self.context = []
|
|
21
|
+
self._append_context("system", context)
|
|
22
|
+
self.tools = {
|
|
23
|
+
"search_internet": search_internet,
|
|
24
|
+
"testing": testing,
|
|
25
|
+
"what_time_is_it": what_time_is_it,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
def talk(self) -> str:
|
|
29
|
+
while True:
|
|
30
|
+
request = input(paint(">>> ").bold().magenta())
|
|
31
|
+
self._append_context("user", request)
|
|
32
|
+
|
|
33
|
+
response = self._generate_content()
|
|
34
|
+
tool_calls = response.message.tool_calls
|
|
35
|
+
content = response.message.content
|
|
36
|
+
self._append_context("assistant", response.message)
|
|
37
|
+
|
|
38
|
+
if tool_calls is not None:
|
|
39
|
+
while True:
|
|
40
|
+
response_tool = self._handle_tool_call(tool_calls)
|
|
41
|
+
self._append_context("assistant", response_tool.message)
|
|
42
|
+
|
|
43
|
+
if response_tool.message.tool_calls is None:
|
|
44
|
+
content = response_tool.message.content
|
|
45
|
+
break
|
|
46
|
+
|
|
47
|
+
tool_calls = response_tool.message.tool_calls
|
|
48
|
+
|
|
49
|
+
print()
|
|
50
|
+
paint(">>> ", end="").bold().green().show()
|
|
51
|
+
print(content)
|
|
52
|
+
print()
|
|
53
|
+
|
|
54
|
+
def _handle_tool_call(self, tool_calls: Sequence) -> ChatResponse:
|
|
55
|
+
for tc in tool_calls:
|
|
56
|
+
result = self.tools[tc.function.name](**tc.function.arguments)
|
|
57
|
+
self._append_context("tool", result, tool_call=tc.function.name)
|
|
58
|
+
|
|
59
|
+
return self._generate_content()
|
|
60
|
+
|
|
61
|
+
def _generate_content(self) -> ChatResponse:
|
|
62
|
+
return chat(
|
|
63
|
+
model=self.model,
|
|
64
|
+
messages=self.context,
|
|
65
|
+
tools=list(self.tools.values()),
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
def _append_context(
|
|
69
|
+
self,
|
|
70
|
+
role: str,
|
|
71
|
+
content: str | Message,
|
|
72
|
+
tool_call: str | None = None,
|
|
73
|
+
):
|
|
74
|
+
if type(content) == Message:
|
|
75
|
+
self.context.append(content)
|
|
76
|
+
return
|
|
77
|
+
|
|
78
|
+
if role == "tool" and tool_call is not None:
|
|
79
|
+
self.context.append({
|
|
80
|
+
"role": role,
|
|
81
|
+
"tool_name": tool_call,
|
|
82
|
+
"content": content,
|
|
83
|
+
})
|
|
84
|
+
return
|
|
85
|
+
|
|
86
|
+
self.context.append({
|
|
87
|
+
"role": role,
|
|
88
|
+
"content": content,
|
|
89
|
+
})
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
|
|
3
|
+
context = f"""
|
|
4
|
+
Today is {datetime.datetime.now()}
|
|
5
|
+
|
|
6
|
+
You are Emilia and you assist Peter
|
|
7
|
+
|
|
8
|
+
You have several tools to facilitate your assisting process:
|
|
9
|
+
|
|
10
|
+
1. search_internet
|
|
11
|
+
2. what_time_is_it
|
|
12
|
+
|
|
13
|
+
1. Use this tool when Peter needs up-to-date accurate information about something, you have to generate a single search request based on Peter's initial question, then, pass the search request to "search_internet" and wait for the results.
|
|
14
|
+
|
|
15
|
+
After getting the results, you provide Peter with a detailed message explaining what you got.
|
|
16
|
+
|
|
17
|
+
2. Use tool whenever you or Peter needs to know what time it is right in the moment.
|
|
18
|
+
"""
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
from ddgs import DDGS
|
|
2
|
+
from ddgs.exceptions import DDGSException
|
|
3
|
+
from trafilatura import (
|
|
4
|
+
fetch_url,
|
|
5
|
+
extract,
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
from kopilogs.paint import paint
|
|
9
|
+
|
|
10
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
11
|
+
import sys
|
|
12
|
+
import textwrap
|
|
13
|
+
import datetime
|
|
14
|
+
|
|
15
|
+
def what_time_is_it() -> str:
|
|
16
|
+
"""
|
|
17
|
+
Use this tool whenever you want to know what time it is right now.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
None
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
str -> Formatted string containing the exact datetime
|
|
24
|
+
"""
|
|
25
|
+
paint(f"Getting datetime...").bold().magenta().show()
|
|
26
|
+
|
|
27
|
+
return datetime.datetime.now().strftime(r"%Y/%m/%d -- %H:%M:%S")
|
|
28
|
+
|
|
29
|
+
def search_internet(search_query: str) -> str:
|
|
30
|
+
"""
|
|
31
|
+
Use this tool when Peter need up-to-date accurate facts about something.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
str -> Search query
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
str -> Formatted str containing webpages: [title, href, information]
|
|
38
|
+
|
|
39
|
+
Use the information retrieved to answer Peter
|
|
40
|
+
"""
|
|
41
|
+
paint(f"Searching the internet... {search_query}").bold().magenta().show()
|
|
42
|
+
|
|
43
|
+
def fetch(result: dict[str, str]) -> str | None:
|
|
44
|
+
extracted = extract(fetch_url(result["href"]))
|
|
45
|
+
return extracted
|
|
46
|
+
|
|
47
|
+
results = list(DDGS().text(search_query, max_results=3))
|
|
48
|
+
with ThreadPoolExecutor() as exec:
|
|
49
|
+
threads = []
|
|
50
|
+
|
|
51
|
+
for result in results:
|
|
52
|
+
thread = exec.submit(fetch, result)
|
|
53
|
+
threads.append(thread)
|
|
54
|
+
|
|
55
|
+
formatted_results = ""
|
|
56
|
+
for i, thread in enumerate(threads):
|
|
57
|
+
try:
|
|
58
|
+
result = thread.result()
|
|
59
|
+
except Exception:
|
|
60
|
+
continue
|
|
61
|
+
|
|
62
|
+
if result is not None:
|
|
63
|
+
formatted_results += textwrap.dedent(
|
|
64
|
+
f"""
|
|
65
|
+
|
|
66
|
+
RESULT {i}
|
|
67
|
+
|
|
68
|
+
{result[:5000]}
|
|
69
|
+
|
|
70
|
+
"""
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
paint("Done.").bold().green().show()
|
|
74
|
+
return formatted_results
|
|
75
|
+
|
|
76
|
+
def testing() -> None:
|
|
77
|
+
"""
|
|
78
|
+
This is a testing tool, only execute this when commanded
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
None
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
None
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
print("Executed testing tool!")
|