exploring-machine 0.0.1__py3-none-any.whl
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.
- exploring_machine/__init__.py +20 -0
- exploring_machine/cli.py +167 -0
- exploring_machine/config.py +33 -0
- exploring_machine/githf.py +71 -0
- exploring_machine/machina.yaml +32 -0
- exploring_machine/machine.py +260 -0
- exploring_machine/providers/__init__.py +7 -0
- exploring_machine/providers/basta.py +82 -0
- exploring_machine/providers/castor_pollux.py +127 -0
- exploring_machine/providers/chinf.py +86 -0
- exploring_machine/providers/depsek.py +82 -0
- exploring_machine/providers/electroid.py +78 -0
- exploring_machine/providers/illuminati.py +87 -0
- exploring_machine/providers/metai.py +164 -0
- exploring_machine/providers/openai.py +82 -0
- exploring_machine/providers/qrog.py +82 -0
- exploring_machine/providers/strangelove.py +81 -0
- exploring_machine/providers/tinker.py +83 -0
- exploring_machine/utilities.py +418 -0
- exploring_machine-0.0.1.dist-info/METADATA +54 -0
- exploring_machine-0.0.1.dist-info/RECORD +25 -0
- exploring_machine-0.0.1.dist-info/WHEEL +5 -0
- exploring_machine-0.0.1.dist-info/entry_points.txt +2 -0
- exploring_machine-0.0.1.dist-info/licenses/LICENSE +21 -0
- exploring_machine-0.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Python
|
|
3
|
+
|
|
4
|
+
"""Copyright (c) Alexander Fedotov.
|
|
5
|
+
This source code is licensed under the license found in the
|
|
6
|
+
LICENSE file in the root directory of this source tree.
|
|
7
|
+
"""
|
|
8
|
+
import urllib.request
|
|
9
|
+
import urllib.error
|
|
10
|
+
import urllib.parse
|
|
11
|
+
import json
|
|
12
|
+
from os import environ
|
|
13
|
+
from ..utilities import messages_to_mpj
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def respond(messages=None, instructions=None, **kwargs):
|
|
17
|
+
"""
|
|
18
|
+
"""
|
|
19
|
+
api_key = environ.get('GEMINI_API_KEY', '')
|
|
20
|
+
api_base = environ.get('GEMINI_API_BASE', 'https://generativelanguage.googleapis.com/v1beta')
|
|
21
|
+
content_model = environ.get('GEMINI_DEFAULT_CONTENT_MODEL', 'gemma-4-31b-it')
|
|
22
|
+
|
|
23
|
+
garbage = [
|
|
24
|
+
{'category': 'HARM_CATEGORY_HATE_SPEECH', 'threshold': 'BLOCK_NONE'},
|
|
25
|
+
{'category': 'HARM_CATEGORY_SEXUALLY_EXPLICIT', 'threshold': 'BLOCK_NONE'},
|
|
26
|
+
{'category': 'HARM_CATEGORY_DANGEROUS_CONTENT', 'threshold': 'BLOCK_NONE'},
|
|
27
|
+
{'category': 'HARM_CATEGORY_HARASSMENT', 'threshold': 'BLOCK_NONE'},
|
|
28
|
+
{'category': 'HARM_CATEGORY_CIVIC_INTEGRITY', 'threshold': 'BLOCK_NONE'}
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
instructions = kwargs.get('system_instruction', instructions)
|
|
32
|
+
system_instruction = dict(role='system', parts=[dict(text=instructions)]) if instructions else None
|
|
33
|
+
|
|
34
|
+
# Trickery for thinking models
|
|
35
|
+
thinking_config = None
|
|
36
|
+
model = kwargs.get("model", content_model)
|
|
37
|
+
if model.startswith('gemini-2.5'):
|
|
38
|
+
thinking_config = {
|
|
39
|
+
'includeThoughts': kwargs.get('include_thoughts', True),
|
|
40
|
+
'thinkingBudget': kwargs.get('thinking_budget', 10000)
|
|
41
|
+
}
|
|
42
|
+
elif model.startswith('gemini-3'):
|
|
43
|
+
thinking_config = {
|
|
44
|
+
'includeThoughts': kwargs.get('include_thoughts', True),
|
|
45
|
+
'thinkingLevel': kwargs.get('thinking_level', 'high')
|
|
46
|
+
}
|
|
47
|
+
elif model.startswith('gemma-4'):
|
|
48
|
+
thinking_config = {
|
|
49
|
+
'includeThoughts': kwargs.get('include_thoughts', True)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
# Define the payload
|
|
53
|
+
payload = {
|
|
54
|
+
'systemInstruction': system_instruction,
|
|
55
|
+
'contents': messages,
|
|
56
|
+
'safetySettings': garbage,
|
|
57
|
+
'generationConfig': {
|
|
58
|
+
'stopSequences': kwargs.get('stop_sequences', ['STOP', 'Title']),
|
|
59
|
+
'responseMimeType': kwargs.get('mime_type', 'text/plain'),
|
|
60
|
+
'responseModalities': kwargs.get('modalities', ['TEXT']),
|
|
61
|
+
'temperature': kwargs.get('temperature', 1.0),
|
|
62
|
+
'maxOutputTokens': kwargs.get('max_tokens', 10000),
|
|
63
|
+
'topP': kwargs.get('top_p', 0.9),
|
|
64
|
+
'topK': kwargs.get('top_k', 10),
|
|
65
|
+
'enableEnhancedCivicAnswers': False,
|
|
66
|
+
},
|
|
67
|
+
}
|
|
68
|
+
if thinking_config:
|
|
69
|
+
payload['generationConfig']['thinkingConfig'] = thinking_config
|
|
70
|
+
if kwargs.get('sources'):
|
|
71
|
+
payload['tools'].append(
|
|
72
|
+
{
|
|
73
|
+
"url_context": {}
|
|
74
|
+
}
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
# Convert data dictionary to JSON and encode it to bytes
|
|
78
|
+
data_bytes = json.dumps(payload).encode('utf-8')
|
|
79
|
+
|
|
80
|
+
# Set the mandatory headers
|
|
81
|
+
headers = {
|
|
82
|
+
"Content-Type": "application/json",
|
|
83
|
+
"User-Agent": "Exploring-Machine"
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
# urlencode parameter
|
|
87
|
+
params = urllib.parse.urlencode({'key': api_key})
|
|
88
|
+
|
|
89
|
+
# Create the Request object
|
|
90
|
+
req = urllib.request.Request(
|
|
91
|
+
f'{api_base}/models/{kwargs.get("model", content_model)}:generateContent?{params}',
|
|
92
|
+
data=data_bytes,
|
|
93
|
+
headers=headers,
|
|
94
|
+
method="POST")
|
|
95
|
+
|
|
96
|
+
try:
|
|
97
|
+
# Execute the request
|
|
98
|
+
with urllib.request.urlopen(req, timeout=300) as response:
|
|
99
|
+
response_data = response.read().decode('utf-8')
|
|
100
|
+
output = json.loads(response_data)
|
|
101
|
+
text = ''
|
|
102
|
+
thoughts = ''
|
|
103
|
+
if output['candidates'][0]['finishReason'] == 'SAFETY':
|
|
104
|
+
raise Exception('Answer censored by Google.')
|
|
105
|
+
for part in output['candidates'][0]['content']['parts']:
|
|
106
|
+
if part.get('thought'):
|
|
107
|
+
thoughts += part['text']
|
|
108
|
+
else:
|
|
109
|
+
text += part['text']
|
|
110
|
+
|
|
111
|
+
return thoughts, text
|
|
112
|
+
|
|
113
|
+
except urllib.error.HTTPError as e:
|
|
114
|
+
# Handle HTTP errors (e.g., 401 Unauthorized, 400 Bad Request)
|
|
115
|
+
error_info = e.read().decode('utf-8', errors='ignore')
|
|
116
|
+
print(f"HTTP Error {e.code}: {e.reason}")
|
|
117
|
+
print(f"Error Details: {error_info}")
|
|
118
|
+
return '', ''
|
|
119
|
+
|
|
120
|
+
except urllib.error.URLError as e:
|
|
121
|
+
# Handle network/connection errors
|
|
122
|
+
print(f"Failed to reach the server: {e.reason}")
|
|
123
|
+
return '', ''
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
if __name__ == '__main__':
|
|
127
|
+
...
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Python
|
|
3
|
+
|
|
4
|
+
"""Copyright (c) Alexander Fedotov.
|
|
5
|
+
This source code is licensed under the license found in the
|
|
6
|
+
LICENSE file in the root directory of this source tree.
|
|
7
|
+
"""
|
|
8
|
+
import json
|
|
9
|
+
import urllib.request
|
|
10
|
+
import urllib.error
|
|
11
|
+
from os import environ
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def respond(messages=None, instructions=None, **kwargs):
|
|
15
|
+
""" All parameters should be in kwargs, but they are optional
|
|
16
|
+
"""
|
|
17
|
+
# The configuration.
|
|
18
|
+
api_key = environ.get("CHINF_API_KEY", '')
|
|
19
|
+
default_model = environ.get("CHINF_DEFAULT_MODEL", 'gpt-5.6-luna')
|
|
20
|
+
api_base = environ.get("CHINF_API_BASE", "https://api.cheaperinference.com/v1")
|
|
21
|
+
|
|
22
|
+
# Set the mandatory headers
|
|
23
|
+
headers = {
|
|
24
|
+
"Content-Type": "application/json",
|
|
25
|
+
"Authorization": f"Bearer {api_key}",
|
|
26
|
+
"X-Api-Key": api_key,
|
|
27
|
+
"Anthropic-Version": "2023-06-01",
|
|
28
|
+
"User-Agent": "chinf"
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
# Receive the instruction
|
|
32
|
+
instruction = kwargs.get('system_instruction', instructions)
|
|
33
|
+
first_message = [dict(role='system', content=instruction)] if instruction else []
|
|
34
|
+
|
|
35
|
+
# add contents and user text to the first (instruction) message
|
|
36
|
+
first_message.extend(messages)
|
|
37
|
+
instruction_and_contents = first_message
|
|
38
|
+
|
|
39
|
+
# Define the initial payload
|
|
40
|
+
payload = {
|
|
41
|
+
"model": kwargs.get("model", default_model),
|
|
42
|
+
"messages": instruction_and_contents,
|
|
43
|
+
"max_tokens": kwargs.get("max_tokens", 132000),
|
|
44
|
+
"reasoning_effort": "max",
|
|
45
|
+
}
|
|
46
|
+
# Convert
|
|
47
|
+
data_bytes = json.dumps(payload).encode('utf-8')
|
|
48
|
+
# Create the Request object
|
|
49
|
+
req = urllib.request.Request(
|
|
50
|
+
f'{api_base}/chat/completions',
|
|
51
|
+
data=data_bytes,
|
|
52
|
+
headers=headers,
|
|
53
|
+
method="POST")
|
|
54
|
+
# Try to query
|
|
55
|
+
try:
|
|
56
|
+
# Execute the request
|
|
57
|
+
with urllib.request.urlopen(req, timeout=3000) as response:
|
|
58
|
+
response_data = response.read().decode('utf-8')
|
|
59
|
+
output = json.loads(response_data)
|
|
60
|
+
|
|
61
|
+
except urllib.error.HTTPError as e:
|
|
62
|
+
# Handle HTTP errors (e.g., 401 Unauthorized, 400 Bad Request)
|
|
63
|
+
error_info = e.read().decode('utf-8', errors='ignore')
|
|
64
|
+
print(f"HTTP Error {e.code}: {e.reason}")
|
|
65
|
+
print(f"Error Details: {error_info}")
|
|
66
|
+
output = {}
|
|
67
|
+
|
|
68
|
+
except urllib.error.URLError as e:
|
|
69
|
+
# Handle network/connection errors
|
|
70
|
+
print(f"Failed to reach the server: {e.reason}")
|
|
71
|
+
output = {}
|
|
72
|
+
# Discern what we got
|
|
73
|
+
result = output.get('choices', {})
|
|
74
|
+
if result:
|
|
75
|
+
completion_message = result[0]['message']
|
|
76
|
+
thoughts = completion_message.get('reasoning', '')
|
|
77
|
+
text = completion_message.get('content', '')
|
|
78
|
+
else:
|
|
79
|
+
thoughts = ''
|
|
80
|
+
text = ''
|
|
81
|
+
|
|
82
|
+
return thoughts, text
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
if __name__ == "__main__":
|
|
86
|
+
...
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Python
|
|
3
|
+
|
|
4
|
+
"""Copyright (c) Alexander Fedotov.
|
|
5
|
+
This source code is licensed under the license found in the
|
|
6
|
+
LICENSE file in the root directory of this source tree.
|
|
7
|
+
"""
|
|
8
|
+
import urllib.request
|
|
9
|
+
import urllib.error
|
|
10
|
+
import json
|
|
11
|
+
from os import environ
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def respond(messages, instructions, **kwargs):
|
|
15
|
+
"""
|
|
16
|
+
"""
|
|
17
|
+
api_key = environ.get("DEPSEK_API_KEY")
|
|
18
|
+
api_base = environ.get("DEPSEK_API_BASE", "https://api.deepseek.com")
|
|
19
|
+
default_model = environ.get("DEPSEK_DEFAULT_MODEL", "deepseek-flash")
|
|
20
|
+
|
|
21
|
+
instruction = kwargs.get('system_instruction', instructions)
|
|
22
|
+
first_message = [dict(role='system', content=instruction)] if instruction else []
|
|
23
|
+
|
|
24
|
+
# add contents and user text to the first (instruction) message
|
|
25
|
+
first_message.extend(messages)
|
|
26
|
+
instruction_and_contents = first_message
|
|
27
|
+
|
|
28
|
+
# Define the payload
|
|
29
|
+
payload = {
|
|
30
|
+
"model": kwargs.get("model", default_model),
|
|
31
|
+
"messages": instruction_and_contents,
|
|
32
|
+
"max_tokens": kwargs.get("max_tokens", 64000),
|
|
33
|
+
"temperature": kwargs.get("temperature", 1.0),
|
|
34
|
+
"reasoning_effort": kwargs.get("reasoning_effort", "max"),
|
|
35
|
+
"thinking": {
|
|
36
|
+
"type": "enabled"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
# Convert data dictionary to JSON and encode it to bytes
|
|
41
|
+
data_bytes = json.dumps(payload).encode('utf-8')
|
|
42
|
+
|
|
43
|
+
# Set the mandatory headers
|
|
44
|
+
headers = {
|
|
45
|
+
"Content-Type": "application/json",
|
|
46
|
+
"Authorization": f"Bearer {api_key}",
|
|
47
|
+
"User-Agent": "Exploring-Machine"
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
# Create the Request object
|
|
51
|
+
req = urllib.request.Request(
|
|
52
|
+
f'{api_base}/chat/completions',
|
|
53
|
+
data=data_bytes,
|
|
54
|
+
headers=headers,
|
|
55
|
+
method="POST")
|
|
56
|
+
|
|
57
|
+
try:
|
|
58
|
+
# Execute the request
|
|
59
|
+
with urllib.request.urlopen(req, timeout=300) as response:
|
|
60
|
+
response_data = response.read().decode('utf-8')
|
|
61
|
+
output = json.loads(response_data)
|
|
62
|
+
message = output['choices'][0]['message']
|
|
63
|
+
text = message['content']
|
|
64
|
+
thoughts = message['reasoning_content']
|
|
65
|
+
|
|
66
|
+
return thoughts, text
|
|
67
|
+
|
|
68
|
+
except urllib.error.HTTPError as e:
|
|
69
|
+
# Handle HTTP errors (e.g., 401 Unauthorized, 400 Bad Request)
|
|
70
|
+
error_info = e.read().decode('utf-8', errors='ignore')
|
|
71
|
+
print(f"HTTP Error {e.code}: {e.reason}")
|
|
72
|
+
print(f"Error Details: {error_info}")
|
|
73
|
+
return '', ''
|
|
74
|
+
|
|
75
|
+
except urllib.error.URLError as e:
|
|
76
|
+
# Handle network/connection errors
|
|
77
|
+
print(f"Failed to reach the server: {e.reason}")
|
|
78
|
+
return '', ''
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
if __name__ == '__main__':
|
|
82
|
+
...
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Python
|
|
3
|
+
|
|
4
|
+
"""Copyright (c) Alexander Fedotov.
|
|
5
|
+
This source code is licensed under the license found in the
|
|
6
|
+
LICENSE file in the root directory of this source tree.
|
|
7
|
+
"""
|
|
8
|
+
import urllib.request
|
|
9
|
+
import urllib.error
|
|
10
|
+
import json
|
|
11
|
+
from os import environ
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def respond(messages=None, instructions=None, **kwargs):
|
|
15
|
+
""" All parameters should be in kwargs, but they are optional
|
|
16
|
+
"""
|
|
17
|
+
api_key = environ.get("ANTHROPIC_API_KEY")
|
|
18
|
+
api_base = environ.get("ANTHROPIC_API_BASE", "https://api.anthropic.com/v1")
|
|
19
|
+
api_type = environ.get("ANTHROPIC_VERSION", "2023-06-01")
|
|
20
|
+
default_model = environ.get("ANTHROPIC_DEFAULT_MODEL", 'claude-opus-4-6')
|
|
21
|
+
|
|
22
|
+
# Define the payload
|
|
23
|
+
payload = {
|
|
24
|
+
"model": kwargs.get("model", default_model),
|
|
25
|
+
"thinking": {"type": "adaptive"},
|
|
26
|
+
"system": kwargs.get("system_instruction", instructions),
|
|
27
|
+
"messages": messages,
|
|
28
|
+
"max_tokens": kwargs.get("max_tokens", 100000),
|
|
29
|
+
"stop_sequences": kwargs.get("stop_sequences", ['stop']),
|
|
30
|
+
"stream": kwargs.get("stream", False),
|
|
31
|
+
"temperature": 1.0,
|
|
32
|
+
"output_config": kwargs.get("output_config", {"effort": "low"}),
|
|
33
|
+
"metadata": kwargs.get("metadata", None)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
# Convert data dictionary to JSON and encode it to bytes
|
|
37
|
+
data_bytes = json.dumps(payload).encode('utf-8')
|
|
38
|
+
|
|
39
|
+
headers = {
|
|
40
|
+
"x-api-key": api_key,
|
|
41
|
+
"anthropic-version": api_type,
|
|
42
|
+
"content-type": "application/json",
|
|
43
|
+
"User-Agent": "Exploring-Machine"
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# Create the Request object
|
|
47
|
+
req = urllib.request.Request(
|
|
48
|
+
f'{api_base}/messages',
|
|
49
|
+
data=data_bytes,
|
|
50
|
+
headers=headers,
|
|
51
|
+
method="POST")
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
# Execute the request
|
|
55
|
+
with urllib.request.urlopen(req, timeout=300) as response:
|
|
56
|
+
response_data = response.read().decode('utf-8')
|
|
57
|
+
output = json.loads(response_data)
|
|
58
|
+
text = ''
|
|
59
|
+
thoughts = ''
|
|
60
|
+
for chunk in output.get("content"):
|
|
61
|
+
if chunk['type'] == 'text':
|
|
62
|
+
addition = chunk['text']
|
|
63
|
+
if addition not in ('\n\n', '\n'):
|
|
64
|
+
text += addition
|
|
65
|
+
|
|
66
|
+
elif chunk['type'] == 'thinking':
|
|
67
|
+
thoughts += chunk['thinking']
|
|
68
|
+
|
|
69
|
+
return thoughts, text
|
|
70
|
+
|
|
71
|
+
except Exception as e:
|
|
72
|
+
print("Unable to generate Message response")
|
|
73
|
+
print(f"Exception: {e}")
|
|
74
|
+
return ['', '']
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
if __name__ == "__main__":
|
|
78
|
+
...
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Python
|
|
3
|
+
|
|
4
|
+
"""Copyright (c) Alexander Fedotov.
|
|
5
|
+
This source code is licensed under the license found in the
|
|
6
|
+
LICENSE file in the root directory of this source tree.
|
|
7
|
+
"""
|
|
8
|
+
import json
|
|
9
|
+
import urllib.request
|
|
10
|
+
import urllib.error
|
|
11
|
+
from os import environ
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def decode_output(output):
|
|
15
|
+
# Parse the result
|
|
16
|
+
text = ''; thoughts = ''
|
|
17
|
+
for part in output:
|
|
18
|
+
part_type = part.get('type', None)
|
|
19
|
+
if part_type == 'message':
|
|
20
|
+
text = " ".join([chunk['text'] for chunk in part['content'] if chunk['type'] == 'output_text'])
|
|
21
|
+
elif part_type == 'reasoning':
|
|
22
|
+
thoughts = " ".join([chunk['text'] for chunk in part['summary'] if chunk['type'] == 'summary_text'])
|
|
23
|
+
function_calls = [part for part in output if part['type'] == 'function_call']
|
|
24
|
+
return thoughts, text, function_calls
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def respond(messages=None, instructions=None, tools=None, **kwargs):
|
|
28
|
+
""" All parameters should be in kwargs, but they are optional
|
|
29
|
+
"""
|
|
30
|
+
# The configuration.
|
|
31
|
+
api_key = environ.get('FIREWORKS_API_KEY')
|
|
32
|
+
api_base = environ.get('FIREWORKS_BASE_URL', 'https://api.fireworks.ai/inference/v1')
|
|
33
|
+
default_model = environ.get('FIREWORKS_MODEL', 'accounts/fireworks/models/kimi-k3')
|
|
34
|
+
|
|
35
|
+
headers = {
|
|
36
|
+
"Content-Type": "application/json",
|
|
37
|
+
"Authorization": "Bearer " + api_key,
|
|
38
|
+
"User-Agent": "Exploring-Machine"
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
# Receive the instruction
|
|
42
|
+
instruction = kwargs.get('system_instruction', instructions)
|
|
43
|
+
|
|
44
|
+
# Define the initial payload
|
|
45
|
+
payload = {
|
|
46
|
+
"model": kwargs.get("model", default_model),
|
|
47
|
+
"instructions": instruction,
|
|
48
|
+
"input": messages,
|
|
49
|
+
"max_output_tokens": kwargs.get("max_tokens", 132000),
|
|
50
|
+
"reasoning": {
|
|
51
|
+
"effort": "max",
|
|
52
|
+
"summary": "detailed"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
# Convert data dictionary to JSON and encode it to bytes
|
|
56
|
+
data_bytes = json.dumps(payload).encode('utf-8')
|
|
57
|
+
# Create the Request object
|
|
58
|
+
req = urllib.request.Request(
|
|
59
|
+
f'{api_base}/responses',
|
|
60
|
+
data=data_bytes,
|
|
61
|
+
headers=headers,
|
|
62
|
+
method="POST")
|
|
63
|
+
# Try to query
|
|
64
|
+
try:
|
|
65
|
+
# Execute the request
|
|
66
|
+
with urllib.request.urlopen(req, timeout=3000) as response:
|
|
67
|
+
response_data = response.read().decode('utf-8')
|
|
68
|
+
output = json.loads(response_data)
|
|
69
|
+
thoughts, text, function_calls = decode_output(output.get('output', {}))
|
|
70
|
+
|
|
71
|
+
except urllib.error.HTTPError as e:
|
|
72
|
+
# Handle HTTP errors (e.g., 401 Unauthorized, 400 Bad Request)
|
|
73
|
+
error_info = e.read().decode('utf-8', errors='ignore')
|
|
74
|
+
print(f"HTTP Error {e.code}: {e.reason}")
|
|
75
|
+
print(f"Error Details: {error_info}")
|
|
76
|
+
return '', ''
|
|
77
|
+
|
|
78
|
+
except urllib.error.URLError as e:
|
|
79
|
+
# Handle network/connection errors
|
|
80
|
+
print(f"Failed to reach the server: {e.reason}")
|
|
81
|
+
return '', ''
|
|
82
|
+
|
|
83
|
+
return thoughts, text
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
if __name__ == "__main__":
|
|
87
|
+
...
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Python
|
|
3
|
+
|
|
4
|
+
"""Copyright (c) Alexander Fedotov.
|
|
5
|
+
This source code is licensed under the license found in the
|
|
6
|
+
LICENSE file in the root directory of this source tree.
|
|
7
|
+
"""
|
|
8
|
+
import urllib.request
|
|
9
|
+
import urllib.error
|
|
10
|
+
import json
|
|
11
|
+
from os import environ
|
|
12
|
+
|
|
13
|
+
api_key = environ.get("METAI_API_KEY", '')
|
|
14
|
+
default_model = environ.get("METAI_DEFAULT_MODEL", 'muse-spark-1.1')
|
|
15
|
+
api_base = environ.get("METAI_API_BASE", "https://api.meta.ai/v1")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# Set the mandatory headers
|
|
19
|
+
headers = {
|
|
20
|
+
"Content-Type": "application/json",
|
|
21
|
+
"Authorization": f"Bearer {api_key}",
|
|
22
|
+
"User-Agent": "Exploring-Machine"
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def get_weather(location):
|
|
27
|
+
# print(f"Executing weather tool for location: {location}")
|
|
28
|
+
return {"temperature": "72F", "condition": "Sunny"}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def query(payload):
|
|
32
|
+
# Convert data dictionary to JSON and encode it to bytes
|
|
33
|
+
data_bytes = json.dumps(payload).encode('utf-8')
|
|
34
|
+
# Create the Request object
|
|
35
|
+
req = urllib.request.Request(
|
|
36
|
+
f'{api_base}/responses',
|
|
37
|
+
data=data_bytes,
|
|
38
|
+
headers=headers,
|
|
39
|
+
method="POST")
|
|
40
|
+
# Try to query
|
|
41
|
+
try:
|
|
42
|
+
# Execute the request
|
|
43
|
+
with urllib.request.urlopen(req, timeout=3000) as response:
|
|
44
|
+
response_data = response.read().decode('utf-8')
|
|
45
|
+
output = json.loads(response_data)
|
|
46
|
+
return output
|
|
47
|
+
|
|
48
|
+
except urllib.error.HTTPError as e:
|
|
49
|
+
# Handle HTTP errors (e.g., 401 Unauthorized, 400 Bad Request)
|
|
50
|
+
error_info = e.read().decode('utf-8', errors='ignore')
|
|
51
|
+
print(f"HTTP Error {e.code}: {e.reason}")
|
|
52
|
+
print(f"Error Details: {error_info}")
|
|
53
|
+
return {}
|
|
54
|
+
|
|
55
|
+
except urllib.error.URLError as e:
|
|
56
|
+
# Handle network/connection errors
|
|
57
|
+
print(f"Failed to reach the server: {e.reason}")
|
|
58
|
+
return {}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def decode_output(output):
|
|
62
|
+
# Parse the result
|
|
63
|
+
text = ''; thoughts = ''
|
|
64
|
+
for part in output:
|
|
65
|
+
part_type = part.get('type', None)
|
|
66
|
+
if part_type == 'message':
|
|
67
|
+
text = " ".join([chunk['text'] for chunk in part['content'] if chunk['type'] == 'output_text'])
|
|
68
|
+
elif part_type == 'reasoning':
|
|
69
|
+
thoughts = " ".join([chunk['text'] for chunk in part['summary'] if chunk['type'] == 'summary_text'])
|
|
70
|
+
function_calls = [part for part in output if part['type'] == 'function_call']
|
|
71
|
+
return thoughts, text, function_calls
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def respond(messages=None, instructions=None, tools=None, **kwargs):
|
|
75
|
+
""" All parameters should be in kwargs, but they are optional
|
|
76
|
+
"""
|
|
77
|
+
# Receive the instruction
|
|
78
|
+
instruction = kwargs.get('system_instruction', instructions)
|
|
79
|
+
|
|
80
|
+
# Define the initial payload
|
|
81
|
+
payload = {
|
|
82
|
+
"model": kwargs.get("model", default_model),
|
|
83
|
+
"instructions": instruction,
|
|
84
|
+
"input": messages,
|
|
85
|
+
"max_output_tokens": kwargs.get("max_tokens", 132000),
|
|
86
|
+
"prompt_cache_retention": "in_memory",
|
|
87
|
+
"include": ["reasoning.encrypted_content"],
|
|
88
|
+
"reasoning": {
|
|
89
|
+
"effort": "high",
|
|
90
|
+
"summary": "detailed"
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
# Tools if there are some
|
|
94
|
+
if tools:
|
|
95
|
+
payload['tools'] = tools
|
|
96
|
+
payload['tool_choice'] = 'auto'
|
|
97
|
+
|
|
98
|
+
while True:
|
|
99
|
+
# Query the API
|
|
100
|
+
result = query(payload)
|
|
101
|
+
# id of the response
|
|
102
|
+
response_id = result['id']
|
|
103
|
+
thoughts, text, function_calls = decode_output(result.get('output', {}))
|
|
104
|
+
|
|
105
|
+
if function_calls:
|
|
106
|
+
function_outputs_messages = []
|
|
107
|
+
for function_call in function_calls:
|
|
108
|
+
call_id = function_call.get('call_id')
|
|
109
|
+
func_name = function_call.get('name')
|
|
110
|
+
func_args_str = function_call.get('arguments', '{}')
|
|
111
|
+
try:
|
|
112
|
+
if isinstance(func_args_str, str):
|
|
113
|
+
func_args = json.loads(func_args_str)
|
|
114
|
+
else:
|
|
115
|
+
func_args = func_args_str
|
|
116
|
+
except Exception as e:
|
|
117
|
+
func_args = {}
|
|
118
|
+
print(f"Error parsing tool arguments for {func_name}: {e}")
|
|
119
|
+
|
|
120
|
+
# Look up tool by name in globals and caller frames
|
|
121
|
+
func = globals().get(func_name)
|
|
122
|
+
if not func:
|
|
123
|
+
import inspect
|
|
124
|
+
frame = inspect.currentframe().f_back
|
|
125
|
+
while frame:
|
|
126
|
+
if func_name in frame.f_globals:
|
|
127
|
+
func = frame.f_globals[func_name]
|
|
128
|
+
break
|
|
129
|
+
frame = frame.f_back
|
|
130
|
+
|
|
131
|
+
if func and callable(func):
|
|
132
|
+
try:
|
|
133
|
+
tool_result = func(**func_args)
|
|
134
|
+
if isinstance(tool_result, (dict, list)):
|
|
135
|
+
result = json.dumps(tool_result)
|
|
136
|
+
else:
|
|
137
|
+
result = str(tool_result)
|
|
138
|
+
except Exception as e:
|
|
139
|
+
result = f"Error executing tool {func_name}: {str(e)}"
|
|
140
|
+
print(result)
|
|
141
|
+
else:
|
|
142
|
+
result = f"Error: Tool function {func_name} not found."
|
|
143
|
+
print(result)
|
|
144
|
+
|
|
145
|
+
tool_message = {
|
|
146
|
+
"type": "function_call_output",
|
|
147
|
+
"call_id": call_id,
|
|
148
|
+
"output": result
|
|
149
|
+
}
|
|
150
|
+
function_outputs_messages.append(tool_message)
|
|
151
|
+
|
|
152
|
+
# Now that all responses have been gathered
|
|
153
|
+
# we can change the payload and send them back
|
|
154
|
+
payload['include'] = [] # must be removed if response_id
|
|
155
|
+
payload['previous_response_id'] = response_id
|
|
156
|
+
payload['input'] = function_outputs_messages
|
|
157
|
+
else:
|
|
158
|
+
break
|
|
159
|
+
|
|
160
|
+
return thoughts, text
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
if __name__ == "__main__":
|
|
164
|
+
...
|