promptstorepy 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.
- promptstorepy-0.1.0/PKG-INFO +17 -0
- promptstorepy-0.1.0/README.md +0 -0
- promptstorepy-0.1.0/promptstorepy/ApiService.py +153 -0
- promptstorepy-0.1.0/promptstorepy/KeycloakAuth.py +47 -0
- promptstorepy-0.1.0/promptstorepy/LangChainCallbackHandler.py +195 -0
- promptstorepy-0.1.0/promptstorepy/PromptStore.py +539 -0
- promptstorepy-0.1.0/promptstorepy/Tracing.py +81 -0
- promptstorepy-0.1.0/promptstorepy/__init__.py +3 -0
- promptstorepy-0.1.0/promptstorepy/utils.py +39 -0
- promptstorepy-0.1.0/pyproject.toml +18 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: promptstorepy
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary:
|
|
5
|
+
Author: Mark Moloney
|
|
6
|
+
Author-email: markmo@me.com
|
|
7
|
+
Requires-Python: >=3.11,<4.0
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
+
Requires-Dist: asyncio (>=3.4.3,<4.0.0)
|
|
12
|
+
Requires-Dist: devtools (>=0.12.2,<0.13.0)
|
|
13
|
+
Requires-Dist: langchain-core (>=0.2.1,<0.3.0)
|
|
14
|
+
Requires-Dist: requests (>=2.32.2,<3.0.0)
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
|
|
17
|
+
|
|
File without changes
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
import os
|
|
4
|
+
import requests
|
|
5
|
+
from requests.exceptions import RequestException
|
|
6
|
+
from urllib.parse import urlencode
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ApiService:
|
|
12
|
+
def __init__(self):
|
|
13
|
+
self.headers = {
|
|
14
|
+
"Accept": "application/json",
|
|
15
|
+
"Content-Type": "application/json",
|
|
16
|
+
}
|
|
17
|
+
self.is_token_refreshing = False
|
|
18
|
+
self.failed_queue = []
|
|
19
|
+
self.state = None
|
|
20
|
+
self.on_refresh_callback = None
|
|
21
|
+
self.on_expiry_callback = None
|
|
22
|
+
|
|
23
|
+
def set_token(self, token):
|
|
24
|
+
self.state = token
|
|
25
|
+
if callable(self.on_refresh_callback):
|
|
26
|
+
self.on_refresh_callback(token)
|
|
27
|
+
|
|
28
|
+
def on_refresh(self, callback):
|
|
29
|
+
self.on_refresh_callback = callback
|
|
30
|
+
|
|
31
|
+
def on_expiry(self, callback):
|
|
32
|
+
self.on_expiry_callback = callback
|
|
33
|
+
|
|
34
|
+
def get_access_token(self):
|
|
35
|
+
return self.state.get("access_token") if self.state else None
|
|
36
|
+
|
|
37
|
+
def get_refresh_token(self):
|
|
38
|
+
return self.state.get("refresh_token") if self.state else None
|
|
39
|
+
|
|
40
|
+
async def renew_token(self):
|
|
41
|
+
refresh_token = self.get_refresh_token()
|
|
42
|
+
token = await self.get_new_token(refresh_token)
|
|
43
|
+
self.set_token(token)
|
|
44
|
+
|
|
45
|
+
async def get_new_token(self, refresh_token):
|
|
46
|
+
host = os.getenv("KEYCLOAK_HOST")
|
|
47
|
+
realm = os.getenv("KEYCLOAK_REALM")
|
|
48
|
+
url = f"{host}/realms/{realm}/protocol/openid-connect/token"
|
|
49
|
+
client_id = os.getenv("KEYCLOAK_CLIENT_ID")
|
|
50
|
+
client_secret = os.getenv("KEYCLOAK_CLIENT_SECRET")
|
|
51
|
+
grant_type = "refresh_token"
|
|
52
|
+
|
|
53
|
+
data = {
|
|
54
|
+
"client_id": client_id,
|
|
55
|
+
"client_secret": client_secret,
|
|
56
|
+
"grant_type": grant_type,
|
|
57
|
+
"refresh_token": refresh_token,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
try:
|
|
61
|
+
response = requests.post(
|
|
62
|
+
url,
|
|
63
|
+
data=urlencode(data),
|
|
64
|
+
headers={
|
|
65
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
66
|
+
},
|
|
67
|
+
)
|
|
68
|
+
response.raise_for_status()
|
|
69
|
+
res_data = response.json()
|
|
70
|
+
return {
|
|
71
|
+
"accessToken": res_data["access_token"],
|
|
72
|
+
"refreshToken": res_data["refresh_token"],
|
|
73
|
+
}
|
|
74
|
+
except RequestException as err:
|
|
75
|
+
message = f"Error getting new token: {err}"
|
|
76
|
+
print(message)
|
|
77
|
+
raise Exception(message)
|
|
78
|
+
|
|
79
|
+
async def process_queue(self, error, access_token=None):
|
|
80
|
+
for prom in self.failed_queue:
|
|
81
|
+
if error:
|
|
82
|
+
prom["reject"](error)
|
|
83
|
+
else:
|
|
84
|
+
prom["resolve"](access_token)
|
|
85
|
+
self.failed_queue = []
|
|
86
|
+
|
|
87
|
+
def request(self, method, url, **kwargs):
|
|
88
|
+
access_token = self.get_access_token()
|
|
89
|
+
headers = kwargs.pop("headers", {})
|
|
90
|
+
if access_token:
|
|
91
|
+
headers["Authorization"] = f"Bearer {access_token}"
|
|
92
|
+
else:
|
|
93
|
+
headers["apikey"] = os.getenv("PROMPTSTORE_API_KEY")
|
|
94
|
+
|
|
95
|
+
headers.update(self.headers)
|
|
96
|
+
kwargs["headers"] = headers
|
|
97
|
+
|
|
98
|
+
try:
|
|
99
|
+
logger.debug(f"url: {url} {kwargs}")
|
|
100
|
+
response = requests.request(method, url, **kwargs, timeout=5)
|
|
101
|
+
logger.debug(f"response: {response}")
|
|
102
|
+
response.raise_for_status()
|
|
103
|
+
return response
|
|
104
|
+
except RequestException as err:
|
|
105
|
+
logger.error(err)
|
|
106
|
+
if err.response and err.response.status_code in [401, 403]:
|
|
107
|
+
return self.handle_token_refresh(err, method, url, **kwargs)
|
|
108
|
+
else:
|
|
109
|
+
raise
|
|
110
|
+
|
|
111
|
+
def handle_token_refresh(self, err, method, url, **kwargs):
|
|
112
|
+
if self.is_token_refreshing:
|
|
113
|
+
future = asyncio.Future()
|
|
114
|
+
self.failed_queue.append(
|
|
115
|
+
{"resolve": future.set_result, "reject": future.set_exception}
|
|
116
|
+
)
|
|
117
|
+
access_token = asyncio.run(future)
|
|
118
|
+
kwargs["headers"]["Authorization"] = f"Bearer {access_token}"
|
|
119
|
+
return self.request(method, url, **kwargs)
|
|
120
|
+
|
|
121
|
+
self.is_token_refreshing = True
|
|
122
|
+
original_request = err.request
|
|
123
|
+
|
|
124
|
+
try:
|
|
125
|
+
refresh_token = self.get_refresh_token()
|
|
126
|
+
token = asyncio.run(self.get_new_token(refresh_token))
|
|
127
|
+
self.set_token(token)
|
|
128
|
+
kwargs["headers"]["Authorization"] = f'Bearer {token["accessToken"]}'
|
|
129
|
+
response = self.request(method, url, **kwargs)
|
|
130
|
+
self.process_queue(None, token["accessToken"])
|
|
131
|
+
return response
|
|
132
|
+
except Exception as err:
|
|
133
|
+
self.process_queue(err)
|
|
134
|
+
if callable(self.on_expiry_callback):
|
|
135
|
+
self.on_expiry_callback(err)
|
|
136
|
+
raise
|
|
137
|
+
finally:
|
|
138
|
+
self.is_token_refreshing = False
|
|
139
|
+
|
|
140
|
+
def get(self, url, **kwargs):
|
|
141
|
+
return self.request("GET", url, **kwargs)
|
|
142
|
+
|
|
143
|
+
def post(self, url, **kwargs):
|
|
144
|
+
return self.request("POST", url, **kwargs)
|
|
145
|
+
|
|
146
|
+
def put(self, url, **kwargs):
|
|
147
|
+
return self.request("PUT", url, **kwargs)
|
|
148
|
+
|
|
149
|
+
def delete(self, url, **kwargs):
|
|
150
|
+
return self.request("DELETE", url, **kwargs)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
service = ApiService()
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import logging
|
|
3
|
+
import requests
|
|
4
|
+
from urllib.parse import urlencode
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class KeycloakAuth:
|
|
8
|
+
|
|
9
|
+
def __init__(self, constants, logger=None):
|
|
10
|
+
self.constants = constants
|
|
11
|
+
self.logger = logger or logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
async def get_access_token(self):
|
|
14
|
+
host = self.constants.get('KEYCLOAK_HOST', os.getenv('KEYCLOAK_HOST'))
|
|
15
|
+
realm = self.constants.get('PROMPTSTORE_KEYCLOAK_REALM', os.getenv('PROMPTSTORE_KEYCLOAK_REALM'))
|
|
16
|
+
client_id = self.constants.get('PROMPTSTORE_KEYCLOAK_CLIENT_ID', os.getenv('PROMPTSTORE_KEYCLOAK_CLIENT_ID'))
|
|
17
|
+
client_secret = self.constants.get('PROMPTSTORE_KEYCLOAK_CLIENT_SECRET', os.getenv('PROMPTSTORE_KEYCLOAK_CLIENT_SECRET'))
|
|
18
|
+
url = f'{host}/realms/{realm}/protocol/openid-connect/token'
|
|
19
|
+
grant_type = 'client_credentials'
|
|
20
|
+
|
|
21
|
+
# self.logger.debug(f"curl -vL -H 'Content-Type: application/x-www-form-urlencoded' {url} -d 'client_id={client_id}&client_secret={client_secret}&grant_type={grant_type}'")
|
|
22
|
+
|
|
23
|
+
data = {
|
|
24
|
+
'client_id': client_id,
|
|
25
|
+
'client_secret': client_secret,
|
|
26
|
+
'grant_type': grant_type,
|
|
27
|
+
}
|
|
28
|
+
try:
|
|
29
|
+
response = requests.post(url, data=urlencode(data), headers={
|
|
30
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
31
|
+
})
|
|
32
|
+
response.raise_for_status()
|
|
33
|
+
return response.json()
|
|
34
|
+
except requests.RequestException as err:
|
|
35
|
+
self.logger.error(err, exc_info=True)
|
|
36
|
+
raise
|
|
37
|
+
|
|
38
|
+
# Usage example:
|
|
39
|
+
# constants = {
|
|
40
|
+
# 'KEYCLOAK_HOST': 'https://keycloak.example.com',
|
|
41
|
+
# 'PROMPTSTORE_KEYCLOAK_REALM': 'myrealm',
|
|
42
|
+
# 'PROMPTSTORE_KEYCLOAK_CLIENT_ID': 'myclientid',
|
|
43
|
+
# 'PROMPTSTORE_KEYCLOAK_CLIENT_SECRET': 'myclientsecret',
|
|
44
|
+
# }
|
|
45
|
+
# logger = logging.getLogger('keycloak_auth')
|
|
46
|
+
# keycloak_auth = KeycloakAuth(constants, logger)
|
|
47
|
+
# access_token = asyncio.run(keycloak_auth.get_access_token())
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
from devtools import debug
|
|
2
|
+
from typing import Any, Dict, List, Optional
|
|
3
|
+
from uuid import UUID
|
|
4
|
+
|
|
5
|
+
from langchain_core.callbacks import BaseCallbackHandler
|
|
6
|
+
from langchain_core.messages.base import BaseMessage
|
|
7
|
+
from langchain_core.outputs.llm_result import LLMResult
|
|
8
|
+
|
|
9
|
+
from .PromptStore import PromptStore
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class LangChainCallbackHandler(BaseCallbackHandler):
|
|
13
|
+
trace_id: str = None
|
|
14
|
+
runs = {}
|
|
15
|
+
|
|
16
|
+
def __init__(self):
|
|
17
|
+
super().__init__()
|
|
18
|
+
self.ps = PromptStore()
|
|
19
|
+
|
|
20
|
+
def get_langchain_run_name(self, serialized: Dict[str, Any], **kwargs: Any) -> str:
|
|
21
|
+
"""Retrieves the 'run_name' for an entity based on Langchain convention, prioritizing the 'name'
|
|
22
|
+
key in 'kwargs' or falling back to the 'name' or 'id' in 'serialized'. Defaults to "<unknown>"
|
|
23
|
+
if none are available.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
serialized (Dict[str, Any]): A dictionary containing the entity's serialized data.
|
|
27
|
+
**kwargs (Any): Additional keyword arguments, potentially including the 'name' override.
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
str: The determined Langchain run name for the entity.
|
|
31
|
+
"""
|
|
32
|
+
# Check if 'name' is in kwargs and not None, otherwise use default fallback logic
|
|
33
|
+
if "name" in kwargs and kwargs["name"] is not None:
|
|
34
|
+
return kwargs["name"]
|
|
35
|
+
|
|
36
|
+
# Fallback to serialized 'name', 'id', or "<unknown>"
|
|
37
|
+
return serialized.get("name", serialized.get("id", ["<unknown>"])[-1])
|
|
38
|
+
|
|
39
|
+
def on_chain_start(
|
|
40
|
+
self,
|
|
41
|
+
serialized: Dict[str, Any],
|
|
42
|
+
inputs: Dict[str, Any],
|
|
43
|
+
*,
|
|
44
|
+
run_id: UUID,
|
|
45
|
+
parent_run_id: Optional[UUID] = None,
|
|
46
|
+
tags: Optional[List[str]] = None,
|
|
47
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
48
|
+
**kwargs: Any,
|
|
49
|
+
) -> Any:
|
|
50
|
+
# debug(inputs)
|
|
51
|
+
# debug(metadata)
|
|
52
|
+
# debug(kwargs)
|
|
53
|
+
# debug(serialized)
|
|
54
|
+
chain_name = kwargs.get("name")
|
|
55
|
+
if chain_name == "AgentExecutor":
|
|
56
|
+
debug(chain_name)
|
|
57
|
+
debug(run_id)
|
|
58
|
+
self.runs[str(run_id)] = chain_name # must come before trace, otherwise `on_chain_end` fires before trace completes and `self.runs` is set
|
|
59
|
+
self.trace_id = self.ps.trace(
|
|
60
|
+
{
|
|
61
|
+
"event": {"type": "trace"},
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
self.ps.trace(
|
|
65
|
+
{
|
|
66
|
+
"id": self.trace_id,
|
|
67
|
+
"event": {
|
|
68
|
+
"type": "agent-start",
|
|
69
|
+
"name": self.get_langchain_run_name(serialized, **kwargs),
|
|
70
|
+
"args": inputs,
|
|
71
|
+
},
|
|
72
|
+
}
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
elif chain_name == "ChatPromptTemplate": # kwargs["run_type"] == "prompt"
|
|
76
|
+
debug(chain_name)
|
|
77
|
+
debug(run_id)
|
|
78
|
+
self.runs[str(run_id)] = chain_name
|
|
79
|
+
self.ps.trace(
|
|
80
|
+
{
|
|
81
|
+
"id": self.trace_id,
|
|
82
|
+
"event": {
|
|
83
|
+
"type": "prompt-start",
|
|
84
|
+
"messageTemplates": {
|
|
85
|
+
"role": "system",
|
|
86
|
+
"content": serialized["kwargs"]["messages"]["kwargs"]["prompt"]["kwargs"]["template"],
|
|
87
|
+
"args": inputs,
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
}
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def on_chain_end(
|
|
95
|
+
self,
|
|
96
|
+
outputs: Dict[str, Any],
|
|
97
|
+
*,
|
|
98
|
+
run_id: UUID,
|
|
99
|
+
parent_run_id: Optional[UUID] = None,
|
|
100
|
+
**kwargs: Any,
|
|
101
|
+
) -> Any:
|
|
102
|
+
# debug(parent_run_id)
|
|
103
|
+
# debug(run_id)
|
|
104
|
+
chain_name = self.runs.get(str(run_id), None)
|
|
105
|
+
# debug(chain_name)
|
|
106
|
+
# debug(outputs)
|
|
107
|
+
# debug(kwargs)
|
|
108
|
+
if chain_name == "AgentExecutor":
|
|
109
|
+
debug(chain_name)
|
|
110
|
+
debug(outputs)
|
|
111
|
+
debug(kwargs)
|
|
112
|
+
self.ps.trace(
|
|
113
|
+
{
|
|
114
|
+
"id": self.trace_id,
|
|
115
|
+
"event": {
|
|
116
|
+
"type": "agent-end",
|
|
117
|
+
"response": outputs,
|
|
118
|
+
},
|
|
119
|
+
}
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
elif chain_name == "ChatPromptTemplate":
|
|
123
|
+
debug(chain_name)
|
|
124
|
+
debug(outputs)
|
|
125
|
+
debug(kwargs)
|
|
126
|
+
self.ps.trace(
|
|
127
|
+
{
|
|
128
|
+
"id": self.trace_id,
|
|
129
|
+
"event": {
|
|
130
|
+
"type": "prompt-end",
|
|
131
|
+
"response": outputs,
|
|
132
|
+
},
|
|
133
|
+
}
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
def on_chat_model_start(
|
|
137
|
+
self,
|
|
138
|
+
serialized: Dict[str, Any],
|
|
139
|
+
messages: List[List[BaseMessage]],
|
|
140
|
+
*,
|
|
141
|
+
run_id: UUID,
|
|
142
|
+
parent_run_id: Optional[UUID] = None,
|
|
143
|
+
tags: Optional[List[str]] = None,
|
|
144
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
145
|
+
**kwargs: Any,
|
|
146
|
+
) -> Any:
|
|
147
|
+
self.ps.trace(
|
|
148
|
+
{
|
|
149
|
+
"id": self.trace_id,
|
|
150
|
+
"event": {
|
|
151
|
+
"type": "prompt-end",
|
|
152
|
+
"messages": messages,
|
|
153
|
+
},
|
|
154
|
+
}
|
|
155
|
+
)
|
|
156
|
+
self.ps.trace(
|
|
157
|
+
{
|
|
158
|
+
"id": self.trace_id,
|
|
159
|
+
"event": {
|
|
160
|
+
"type": "model-start",
|
|
161
|
+
"model": serialized["kwargs"]["model_name"],
|
|
162
|
+
"modelParams": {
|
|
163
|
+
"temperature": serialized["kwargs"]["temperature"],
|
|
164
|
+
"n": serialized["kwargs"]["n"],
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
}
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
def on_llm_end(
|
|
171
|
+
self,
|
|
172
|
+
response: LLMResult,
|
|
173
|
+
*,
|
|
174
|
+
run_id: UUID,
|
|
175
|
+
parent_run_id: Optional[UUID] = None,
|
|
176
|
+
**kwargs: Any,
|
|
177
|
+
) -> Any:
|
|
178
|
+
self.ps.trace(
|
|
179
|
+
{
|
|
180
|
+
"id": self.trace_id,
|
|
181
|
+
"event": {
|
|
182
|
+
"type": "model-end",
|
|
183
|
+
"response": response,
|
|
184
|
+
},
|
|
185
|
+
}
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
def on_llm_error(
|
|
189
|
+
error: BaseException,
|
|
190
|
+
*,
|
|
191
|
+
run_id: UUID,
|
|
192
|
+
parent_run_id: Optional[UUID] = None,
|
|
193
|
+
**kwargs: Any,
|
|
194
|
+
) -> Any:
|
|
195
|
+
pass
|
|
@@ -0,0 +1,539 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import json
|
|
3
|
+
import logging
|
|
4
|
+
import requests
|
|
5
|
+
from devtools import debug
|
|
6
|
+
from uuid import uuid4
|
|
7
|
+
|
|
8
|
+
from .ApiService import ApiService
|
|
9
|
+
from .KeycloakAuth import KeycloakAuth
|
|
10
|
+
from .utils import get_search_type, strip_empty_values
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class PromptStore:
|
|
14
|
+
def __init__(self, constants=None, logger=None, callback=None):
|
|
15
|
+
self.constants = constants or {}
|
|
16
|
+
self.logger = logger or logging.getLogger(__name__)
|
|
17
|
+
self.url = self.constants.get(
|
|
18
|
+
"PROMPTSTORE_BASE_URL", os.getenv("PROMPTSTORE_BASE_URL")
|
|
19
|
+
)
|
|
20
|
+
self.workspace_id = self.constants.get(
|
|
21
|
+
"WORKSPACE_ID", os.getenv("WORKSPACE_ID")
|
|
22
|
+
)
|
|
23
|
+
self.functions = {}
|
|
24
|
+
self.api_service = ApiService()
|
|
25
|
+
|
|
26
|
+
auth_method = self.constants.get("AUTH_METHOD", os.getenv("AUTH_METHOD"))
|
|
27
|
+
if auth_method and auth_method.lower() == "oauth":
|
|
28
|
+
self.auth = KeycloakAuth(self.constants, self.logger)
|
|
29
|
+
token = self.auth.get_access_token()
|
|
30
|
+
self.api_service.set_token(token)
|
|
31
|
+
if callable(callback):
|
|
32
|
+
callback()
|
|
33
|
+
|
|
34
|
+
def trace(self, event):
|
|
35
|
+
debug(f"trace [event={json.dumps(event)}]")
|
|
36
|
+
self.logger.debug(f"trace [event={json.dumps(event)}]")
|
|
37
|
+
try:
|
|
38
|
+
url = f"{self.url}/traces"
|
|
39
|
+
event = {**event, "workspaceId": self.workspace_id}
|
|
40
|
+
res = self.api_service.post(url, json=event)
|
|
41
|
+
# debug(res.json())
|
|
42
|
+
return res.json()
|
|
43
|
+
except requests.RequestException as err:
|
|
44
|
+
debug(err)
|
|
45
|
+
self.logger.error(str(err))
|
|
46
|
+
if err.response and err.response.json().get("errors"):
|
|
47
|
+
return err.response.json()
|
|
48
|
+
return {"errors": [{"message": str(err)}]}
|
|
49
|
+
|
|
50
|
+
def add_function(self, name, params, content_only=False):
|
|
51
|
+
self.logger.debug(
|
|
52
|
+
f"addFunction [name={name}; params={json.dumps(params)}; contentOnly={content_only}]"
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
async def func(args, param_overrides=None):
|
|
56
|
+
param_overrides = param_overrides or {}
|
|
57
|
+
try:
|
|
58
|
+
url = f"{self.url}/executions/{name}"
|
|
59
|
+
options = {
|
|
60
|
+
"args": args,
|
|
61
|
+
"params": {**params, **strip_empty_values(param_overrides)},
|
|
62
|
+
"workspaceId": self.workspace_id,
|
|
63
|
+
}
|
|
64
|
+
self.logger.debug(f"url: {url}, options: {options}")
|
|
65
|
+
res = self.api_service.post(url, json=options)
|
|
66
|
+
data = res.json()
|
|
67
|
+
if data.get("errors"):
|
|
68
|
+
return {"errors": data["errors"]}
|
|
69
|
+
if content_only:
|
|
70
|
+
message = data["response"]["choices"][0]["message"]
|
|
71
|
+
if message.get("function_call"):
|
|
72
|
+
try:
|
|
73
|
+
return json.loads(message["function_call"]["arguments"])
|
|
74
|
+
except json.JSONDecodeError as err:
|
|
75
|
+
self.logger.error(f"error parsing json response: {err}")
|
|
76
|
+
return {}
|
|
77
|
+
else:
|
|
78
|
+
return message["content"]
|
|
79
|
+
return data
|
|
80
|
+
except requests.RequestException as err:
|
|
81
|
+
self.logger.error(str(err))
|
|
82
|
+
if err.response and err.response.json().get("errors"):
|
|
83
|
+
return err.response.json()
|
|
84
|
+
return {"errors": [{"message": str(err)}]}
|
|
85
|
+
|
|
86
|
+
setattr(self, name, func)
|
|
87
|
+
return func
|
|
88
|
+
|
|
89
|
+
def execute(self, name, args, params, content_only=False):
|
|
90
|
+
self.logger.debug(
|
|
91
|
+
f"execute [name={name}; args={json.dumps(args)}; params={json.dumps(params)}; contentOnly={content_only}]"
|
|
92
|
+
)
|
|
93
|
+
try:
|
|
94
|
+
url = f"{self.url}/executions/{name}"
|
|
95
|
+
options = {
|
|
96
|
+
"args": args,
|
|
97
|
+
"params": params,
|
|
98
|
+
"workspaceId": self.workspace_id,
|
|
99
|
+
}
|
|
100
|
+
self.logger.debug(f"url: {url}, options: {options}")
|
|
101
|
+
res = self.api_service.post(url, json=options)
|
|
102
|
+
data = res.json()
|
|
103
|
+
if data.get("errors"):
|
|
104
|
+
return {"errors": data["errors"]}
|
|
105
|
+
if content_only:
|
|
106
|
+
message = data["response"]["choices"][0]["message"]
|
|
107
|
+
if message.get("function_call"):
|
|
108
|
+
try:
|
|
109
|
+
return json.loads(message["function_call"]["arguments"])
|
|
110
|
+
except json.JSONDecodeError as err:
|
|
111
|
+
self.logger.error(f"error parsing json response: {err}")
|
|
112
|
+
return {}
|
|
113
|
+
else:
|
|
114
|
+
return message["content"]
|
|
115
|
+
return data
|
|
116
|
+
except requests.RequestException as err:
|
|
117
|
+
self.logger.error(str(err))
|
|
118
|
+
if err.response and err.response.json().get("errors"):
|
|
119
|
+
return err.response.json()
|
|
120
|
+
return {"errors": [{"message": str(err)}]}
|
|
121
|
+
|
|
122
|
+
def execute_composition(self, name, args, params, functions):
|
|
123
|
+
self.logger.debug(
|
|
124
|
+
f"executeComposition [name={name}; args={json.dumps(args)}; params={json.dumps(params)}; functions={json.dumps(functions)}]"
|
|
125
|
+
)
|
|
126
|
+
try:
|
|
127
|
+
url = f"{self.url}/composition-executions/{name}"
|
|
128
|
+
options = {
|
|
129
|
+
"args": args,
|
|
130
|
+
"params": params,
|
|
131
|
+
"functions": functions,
|
|
132
|
+
"workspaceId": self.workspace_id,
|
|
133
|
+
}
|
|
134
|
+
self.logger.debug(f"url: {url}, options: {options}")
|
|
135
|
+
res = self.api_service.post(url, json=options)
|
|
136
|
+
return res.json()
|
|
137
|
+
except requests.RequestException as err:
|
|
138
|
+
self.logger.error(str(err))
|
|
139
|
+
if err.response and err.response.json().get("errors"):
|
|
140
|
+
return err.response.json()
|
|
141
|
+
return {"errors": [{"message": str(err)}]}
|
|
142
|
+
|
|
143
|
+
def call_tool(self, name, args, raw):
|
|
144
|
+
self.logger.debug(f"callTool [name={name}; args={json.dumps(args)}; raw={raw}]")
|
|
145
|
+
try:
|
|
146
|
+
url = f"{self.url}/tool-executions/{name}"
|
|
147
|
+
options = {"args": args, "raw": raw}
|
|
148
|
+
self.logger.debug(f"url: {url}, options: {options}")
|
|
149
|
+
res = self.api_service.post(url, json=options)
|
|
150
|
+
return res.json()
|
|
151
|
+
except requests.RequestException as err:
|
|
152
|
+
self.logger.error(str(err))
|
|
153
|
+
if err.response and err.response.json().get("errors"):
|
|
154
|
+
return err.response.json()
|
|
155
|
+
return {"errors": [{"message": str(err)}]}
|
|
156
|
+
|
|
157
|
+
def chat_completion(self, messages, model, max_tokens, index_name):
|
|
158
|
+
self.logger.debug(
|
|
159
|
+
f"chatCompletion [messages={json.dumps(messages)}; model={model}; maxTokens={max_tokens}; indexName={index_name}]"
|
|
160
|
+
)
|
|
161
|
+
try:
|
|
162
|
+
url = f"{self.url}/chat"
|
|
163
|
+
options = {
|
|
164
|
+
"messages": messages,
|
|
165
|
+
"model": model,
|
|
166
|
+
"maxTokens": max_tokens,
|
|
167
|
+
"indexName": index_name,
|
|
168
|
+
"workspaceId": self.workspace_id,
|
|
169
|
+
}
|
|
170
|
+
self.logger.debug(f"url: {url}, options: {options}")
|
|
171
|
+
res = self.api_service.post(url, json=options)
|
|
172
|
+
return res.json()
|
|
173
|
+
except requests.RequestException as err:
|
|
174
|
+
self.logger.error(str(err))
|
|
175
|
+
if err.response and err.response.json().get("errors"):
|
|
176
|
+
return err.response.json()
|
|
177
|
+
return {"errors": [{"message": str(err)}]}
|
|
178
|
+
|
|
179
|
+
def create_embeddings(self, embedding_provider, embedding_model, chunks):
|
|
180
|
+
self.logger.debug(
|
|
181
|
+
f"createEmbeddings [embeddingProvider={embedding_provider}; embeddingModel={embedding_model}]"
|
|
182
|
+
)
|
|
183
|
+
try:
|
|
184
|
+
url = f"{self.url}/embeddings/{embedding_provider}"
|
|
185
|
+
options = {
|
|
186
|
+
"chunks": chunks,
|
|
187
|
+
"embeddingModel": embedding_model,
|
|
188
|
+
"embeddingProvider": embedding_provider,
|
|
189
|
+
}
|
|
190
|
+
self.logger.debug(f"url: {url}, options: {options}")
|
|
191
|
+
res = self.api_service.post(url, json=options)
|
|
192
|
+
return res.json()
|
|
193
|
+
except requests.RequestException as err:
|
|
194
|
+
self.logger.error(str(err))
|
|
195
|
+
if err.response and err.response.json().get("errors"):
|
|
196
|
+
return err.response.json()
|
|
197
|
+
return {"errors": [{"message": str(err)}]}
|
|
198
|
+
|
|
199
|
+
def create_image(self, prompt, n=1, size="1024x1024"):
|
|
200
|
+
self.logger.debug(f"createImage [prompt={prompt}; n={n}; size={size}]")
|
|
201
|
+
try:
|
|
202
|
+
url = f"{self.url}/image-request"
|
|
203
|
+
options = {
|
|
204
|
+
"sourceId": self.workspace_id,
|
|
205
|
+
"prompt": prompt,
|
|
206
|
+
"size": size,
|
|
207
|
+
"n": n,
|
|
208
|
+
}
|
|
209
|
+
self.logger.debug(f"url: {url}, options: {options}")
|
|
210
|
+
res = self.api_service.post(url, json=options)
|
|
211
|
+
return res.json()
|
|
212
|
+
except requests.RequestException as err:
|
|
213
|
+
self.logger.error(str(err))
|
|
214
|
+
if err.response and err.response.json().get("errors"):
|
|
215
|
+
return err.response.json()
|
|
216
|
+
return {"errors": [{"message": str(err)}]}
|
|
217
|
+
|
|
218
|
+
def generate_image_variant(self, image_url, n=1):
|
|
219
|
+
self.logger.debug(f"generateImageVariant [imageUrl={image_url}; n={n}]")
|
|
220
|
+
try:
|
|
221
|
+
url = f"{self.url}/gen-image-variant"
|
|
222
|
+
options = {"imageUrl": image_url, "n": n, "workspaceId": self.workspace_id}
|
|
223
|
+
self.logger.debug(f"url: {url}, options: {options}")
|
|
224
|
+
res = self.api_service.post(url, json=options)
|
|
225
|
+
return res.json()
|
|
226
|
+
except requests.RequestException as err:
|
|
227
|
+
self.logger.error(str(err))
|
|
228
|
+
if err.response and err.response.json().get("errors"):
|
|
229
|
+
return err.response.json()
|
|
230
|
+
return {"errors": [{"message": str(err)}]}
|
|
231
|
+
|
|
232
|
+
def get_functions_by_tag(self, tag):
|
|
233
|
+
self.logger.debug(
|
|
234
|
+
f"getFunctionsByTag [workspaceId={self.workspace_id}; tag={tag}]"
|
|
235
|
+
)
|
|
236
|
+
try:
|
|
237
|
+
url = f"{self.url}/workspaces/{self.workspace_id}/functions/tags/{tag}"
|
|
238
|
+
self.logger.debug(f"url: {url}")
|
|
239
|
+
res = self.api_service.get(url)
|
|
240
|
+
return res.json()
|
|
241
|
+
except requests.RequestException as err:
|
|
242
|
+
self.logger.error(str(err))
|
|
243
|
+
if err.response and err.response.json().get("errors"):
|
|
244
|
+
return err.response.json()
|
|
245
|
+
return {"errors": [{"message": str(err)}]}
|
|
246
|
+
|
|
247
|
+
def get_compositions(self):
|
|
248
|
+
self.logger.debug(f"getCompositions [workspaceId={self.workspace_id}]")
|
|
249
|
+
try:
|
|
250
|
+
url = f"{self.url}/workspaces/{self.workspace_id}/compositions"
|
|
251
|
+
self.logger.debug(f"url: {url}")
|
|
252
|
+
res = self.api_service.get(url)
|
|
253
|
+
return res.json()
|
|
254
|
+
except requests.RequestException as err:
|
|
255
|
+
self.logger.error(str(err))
|
|
256
|
+
if err.response and err.response.json().get("errors"):
|
|
257
|
+
return err.response.json()
|
|
258
|
+
return {"errors": [{"message": str(err)}]}
|
|
259
|
+
|
|
260
|
+
def get_composition(self, composition_id):
|
|
261
|
+
self.logger.debug(f"getComposition [id={composition_id}]")
|
|
262
|
+
try:
|
|
263
|
+
url = f"{self.url}/compositions/{composition_id}"
|
|
264
|
+
self.logger.debug(f"url: {url}")
|
|
265
|
+
res = self.api_service.get(url)
|
|
266
|
+
return res.json()
|
|
267
|
+
except requests.RequestException as err:
|
|
268
|
+
self.logger.error(str(err))
|
|
269
|
+
if err.response and err.response.json().get("errors"):
|
|
270
|
+
return err.response.json()
|
|
271
|
+
return {"errors": [{"message": str(err)}]}
|
|
272
|
+
|
|
273
|
+
def get_prompt_set(self, skill: str):
|
|
274
|
+
self.logger.debug(f"getPromptSet [skill={skill}]")
|
|
275
|
+
try:
|
|
276
|
+
url = f"{self.url}/workspaces/{self.workspace_id}/prompt-sets?skill={skill}"
|
|
277
|
+
self.logger.debug(f"url: {url}")
|
|
278
|
+
res = self.api_service.get(url)
|
|
279
|
+
pss = res.json()
|
|
280
|
+
self.logger.debug(f"pss: {pss}")
|
|
281
|
+
self.logger.debug(f"len: {len(pss)}")
|
|
282
|
+
if len(pss) > 0:
|
|
283
|
+
return pss[0]
|
|
284
|
+
return None
|
|
285
|
+
except requests.RequestException as err:
|
|
286
|
+
self.logger.error(str(err))
|
|
287
|
+
if err.response and err.response.json().get("errors"):
|
|
288
|
+
return err.response.json()
|
|
289
|
+
return {"errors": [{"message": str(err)}]}
|
|
290
|
+
|
|
291
|
+
def get_prompt_sets(self):
|
|
292
|
+
self.logger.debug("getPromptSets")
|
|
293
|
+
try:
|
|
294
|
+
url = f"{self.url}/prompt-sets"
|
|
295
|
+
self.logger.debug(f"url: {url}")
|
|
296
|
+
res = self.api_service.get(url)
|
|
297
|
+
return res.json()
|
|
298
|
+
except requests.RequestException as err:
|
|
299
|
+
self.logger.error(str(err))
|
|
300
|
+
if err.response and err.response.json().get("errors"):
|
|
301
|
+
return err.response.json()
|
|
302
|
+
return {"errors": [{"message": str(err)}]}
|
|
303
|
+
|
|
304
|
+
def get_workspace_prompt_sets(self):
|
|
305
|
+
self.logger.debug(f"getWorkspacePromptSets [workspaceId={self.workspace_id}]")
|
|
306
|
+
try:
|
|
307
|
+
url = f"{self.url}/workspaces/{self.workspace_id}/prompt-sets"
|
|
308
|
+
self.logger.debug(f"url: {url}")
|
|
309
|
+
res = self.api_service.get(url)
|
|
310
|
+
return res.json()
|
|
311
|
+
except requests.RequestException as err:
|
|
312
|
+
self.logger.error(str(err))
|
|
313
|
+
if err.response and err.response.json().get("errors"):
|
|
314
|
+
return err.response.json()
|
|
315
|
+
return {"errors": [{"message": str(err)}]}
|
|
316
|
+
|
|
317
|
+
def get_workspace_prompt_sets_by_skill(self, skill):
|
|
318
|
+
self.logger.debug(
|
|
319
|
+
f"getWorkspacePromptSetsBySkill [workspaceId={self.workspace_id}; skill={skill}]"
|
|
320
|
+
)
|
|
321
|
+
try:
|
|
322
|
+
url = f"{self.url}/workspaces/{self.workspace_id}/prompt-sets?skill={skill}"
|
|
323
|
+
self.logger.debug(f"url: {url}")
|
|
324
|
+
res = self.api_service.get(url)
|
|
325
|
+
return res.json()
|
|
326
|
+
except requests.RequestException as err:
|
|
327
|
+
self.logger.error(str(err))
|
|
328
|
+
if err.response and err.response.json().get("errors"):
|
|
329
|
+
return err.response.json()
|
|
330
|
+
return {"errors": [{"message": str(err)}]}
|
|
331
|
+
|
|
332
|
+
def get_indexes(self):
|
|
333
|
+
self.logger.debug(f"getIndexes [workspaceId={self.workspace_id}]")
|
|
334
|
+
try:
|
|
335
|
+
url = f"{self.url}/workspaces/{self.workspace_id}/indexes"
|
|
336
|
+
self.logger.debug(f"url: {url}")
|
|
337
|
+
res = self.api_service.get(url)
|
|
338
|
+
return res.json()
|
|
339
|
+
except requests.RequestException as err:
|
|
340
|
+
self.logger.error(str(err))
|
|
341
|
+
if err.response and err.response.json().get("errors"):
|
|
342
|
+
return err.response.json()
|
|
343
|
+
return {"errors": [{"message": str(err)}]}
|
|
344
|
+
|
|
345
|
+
def get_index(self, vector_store_provider, index_name):
|
|
346
|
+
self.logger.debug(
|
|
347
|
+
f"getIndex [vectorStoreProvider={vector_store_provider}; indexName={index_name}]"
|
|
348
|
+
)
|
|
349
|
+
try:
|
|
350
|
+
url = f"{self.url}/index/{vector_store_provider}/{index_name}"
|
|
351
|
+
self.logger.debug(f"url: {url}")
|
|
352
|
+
res = self.api_service.get(url)
|
|
353
|
+
return res.json()
|
|
354
|
+
except requests.RequestException as err:
|
|
355
|
+
self.logger.error(str(err))
|
|
356
|
+
if err.response and err.response.json().get("errors"):
|
|
357
|
+
return err.response.json()
|
|
358
|
+
return {"errors": [{"message": str(err)}]}
|
|
359
|
+
|
|
360
|
+
def create_index(self, vector_store_provider, index_name, schema, params):
|
|
361
|
+
self.logger.debug(
|
|
362
|
+
f"createIndex [vectorStoreProvider={vector_store_provider}; indexName={index_name}; schema={json.dumps(schema)}; params={json.dumps(params)}]"
|
|
363
|
+
)
|
|
364
|
+
try:
|
|
365
|
+
url = f"{self.url}/index"
|
|
366
|
+
options = {
|
|
367
|
+
"indexName": index_name,
|
|
368
|
+
"schema": schema,
|
|
369
|
+
"params": params,
|
|
370
|
+
"vectorStoreProvider": vector_store_provider,
|
|
371
|
+
}
|
|
372
|
+
self.logger.debug(f"url: {url}, options: {options}")
|
|
373
|
+
res = self.api_service.post(url, json=options)
|
|
374
|
+
return res.json()
|
|
375
|
+
except requests.RequestException as err:
|
|
376
|
+
self.logger.error(str(err))
|
|
377
|
+
if err.response and err.response.json().get("errors"):
|
|
378
|
+
return err.response.json()
|
|
379
|
+
return {"errors": [{"message": str(err)}]}
|
|
380
|
+
|
|
381
|
+
def drop_index(self, vector_store_provider, index_name):
|
|
382
|
+
self.logger.debug(
|
|
383
|
+
f"dropIndex [vectorStoreProvider={vector_store_provider}; indexName={index_name}]"
|
|
384
|
+
)
|
|
385
|
+
try:
|
|
386
|
+
url = f"{self.url}/index/{vector_store_provider}/{index_name}"
|
|
387
|
+
self.logger.debug(f"url: {url}")
|
|
388
|
+
res = self.api_service.delete(url)
|
|
389
|
+
return res.json()
|
|
390
|
+
except requests.RequestException as err:
|
|
391
|
+
self.logger.error(str(err))
|
|
392
|
+
if err.response and err.response.json().get("errors"):
|
|
393
|
+
return err.response.json()
|
|
394
|
+
return {"errors": [{"message": str(err)}]}
|
|
395
|
+
|
|
396
|
+
def add_document(self, filepath):
|
|
397
|
+
self.logger.debug(f"addDocument [filepath={filepath}]")
|
|
398
|
+
try:
|
|
399
|
+
correlation_id = str(uuid4())
|
|
400
|
+
filename = os.path.basename(filepath)
|
|
401
|
+
mimetype = mimetypes.guess_type(filepath)[0]
|
|
402
|
+
with open(filepath, "rb") as file:
|
|
403
|
+
files = {
|
|
404
|
+
"file": (filename, file, mimetype),
|
|
405
|
+
}
|
|
406
|
+
data = {
|
|
407
|
+
"sourceId": self.workspace_id,
|
|
408
|
+
"correlationId": correlation_id,
|
|
409
|
+
}
|
|
410
|
+
url = f"{self.url}/upload/"
|
|
411
|
+
self.logger.debug(f"url: {url}, files: {files}, data: {data}")
|
|
412
|
+
res = self.api_service.post(url, files=files, data=data)
|
|
413
|
+
self.logger.debug("File uploaded to document service successfully.")
|
|
414
|
+
return res.json()
|
|
415
|
+
except requests.RequestException as err:
|
|
416
|
+
self.logger.error(str(err))
|
|
417
|
+
if err.response and err.response.json().get("errors"):
|
|
418
|
+
return err.response.json()
|
|
419
|
+
return {"errors": [{"message": str(err)}]}
|
|
420
|
+
|
|
421
|
+
def index_documents(self, index_name, documents):
|
|
422
|
+
self.logger.debug(f"indexDocuments [indexName={index_name}]")
|
|
423
|
+
try:
|
|
424
|
+
url = f"{self.url}/documents"
|
|
425
|
+
self.logger.debug(f"url: {url}")
|
|
426
|
+
res = self.api_service.post(
|
|
427
|
+
url, json={"indexName": index_name, "documents": documents}
|
|
428
|
+
)
|
|
429
|
+
return res.json()
|
|
430
|
+
except requests.RequestException as err:
|
|
431
|
+
self.logger.error(str(err))
|
|
432
|
+
if err.response and err.response.json().get("errors"):
|
|
433
|
+
return err.response.json()
|
|
434
|
+
return {"errors": [{"message": str(err)}]}
|
|
435
|
+
|
|
436
|
+
def drop_documents(self, index_name, query, attrs):
|
|
437
|
+
self.logger.debug(
|
|
438
|
+
f"dropDocuments [indexName={index_name}; query={query}; attrs={json.dumps(attrs)}]"
|
|
439
|
+
)
|
|
440
|
+
try:
|
|
441
|
+
query_params = "&".join([f"{k}={v}" for k, v in attrs.items()])
|
|
442
|
+
url = f"{self.url}/delete-matching?indexName={index_name}"
|
|
443
|
+
if query:
|
|
444
|
+
url += f"&q={query}"
|
|
445
|
+
if query_params:
|
|
446
|
+
url += f"&{query_params}"
|
|
447
|
+
self.logger.debug(f"url: {url}")
|
|
448
|
+
res = self.api_service.delete(url)
|
|
449
|
+
return res.json()
|
|
450
|
+
except requests.RequestException as err:
|
|
451
|
+
self.logger.error(str(err))
|
|
452
|
+
if err.response and err.response.json().get("errors"):
|
|
453
|
+
return err.response.json()
|
|
454
|
+
return {"errors": [{"message": str(err)}]}
|
|
455
|
+
|
|
456
|
+
def index_chunks(self, vector_store_provider, chunks, embeddings, params):
|
|
457
|
+
self.logger.debug(
|
|
458
|
+
f"indexChunks [vectorStoreProvider={vector_store_provider}; params={json.dumps(params)}]"
|
|
459
|
+
)
|
|
460
|
+
try:
|
|
461
|
+
url = f'{self.url}/chunks/{vector_store_provider}/{params["indexName"]}'
|
|
462
|
+
options = {"chunks": chunks, "embeddings": embeddings, "params": params}
|
|
463
|
+
self.logger.debug(f"url: {url}, options: {options}")
|
|
464
|
+
res = self.api_service.post(url, json=options)
|
|
465
|
+
return res.json()
|
|
466
|
+
except requests.RequestException as err:
|
|
467
|
+
self.logger.error(str(err))
|
|
468
|
+
if err.response and err.response.json().get("errors"):
|
|
469
|
+
return err.response.json()
|
|
470
|
+
return {"errors": [{"message": str(err)}]}
|
|
471
|
+
|
|
472
|
+
def delete_chunks(self, vector_store_provider, ids, params):
|
|
473
|
+
self.logger.debug(
|
|
474
|
+
f"deleteChunks [vectorStoreProvider={vector_store_provider}; ids={ids}; params={json.dumps(params)}]"
|
|
475
|
+
)
|
|
476
|
+
try:
|
|
477
|
+
url = f"{self.url}/bulk-delete"
|
|
478
|
+
options = {
|
|
479
|
+
"ids": ids,
|
|
480
|
+
"params": params,
|
|
481
|
+
"vectorStoreProvider": vector_store_provider,
|
|
482
|
+
}
|
|
483
|
+
self.logger.debug(f"url: {url}, options: {options}")
|
|
484
|
+
res = self.api_service.post(url, json=options)
|
|
485
|
+
return res.json()
|
|
486
|
+
except requests.RequestException as err:
|
|
487
|
+
self.logger.error(str(err))
|
|
488
|
+
if err.response and err.response.json().get("errors"):
|
|
489
|
+
return err.response.json()
|
|
490
|
+
return {"errors": [{"message": str(err)}]}
|
|
491
|
+
|
|
492
|
+
def delete_chunk(self, vector_store_provider, chunk_id, params):
|
|
493
|
+
self.logger.debug(
|
|
494
|
+
f"deleteChunk [vectorStoreProvider={vector_store_provider}; id={chunk_id}; params={json.dumps(params)}]"
|
|
495
|
+
)
|
|
496
|
+
try:
|
|
497
|
+
url = f"{self.url}/bulk-delete"
|
|
498
|
+
options = {
|
|
499
|
+
"ids": [chunk_id],
|
|
500
|
+
"params": params,
|
|
501
|
+
"vectorStoreProvider": vector_store_provider,
|
|
502
|
+
}
|
|
503
|
+
self.logger.debug(f"url: {url}, options: {options}")
|
|
504
|
+
res = self.api_service.post(url, json=options)
|
|
505
|
+
return res.json()
|
|
506
|
+
except requests.RequestException as err:
|
|
507
|
+
self.logger.error(str(err))
|
|
508
|
+
if err.response and err.response.json().get("errors"):
|
|
509
|
+
return err.response.json()
|
|
510
|
+
return {"errors": [{"message": str(err)}]}
|
|
511
|
+
|
|
512
|
+
def search(self, params):
|
|
513
|
+
self.logger.debug(f"search [params={json.dumps(params)}]")
|
|
514
|
+
try:
|
|
515
|
+
url = f"{self.url}/search"
|
|
516
|
+
options = {**params, "workspaceId": 1}
|
|
517
|
+
self.logger.debug(f"url: {url}, options: {options}")
|
|
518
|
+
res = self.api_service.post(url, json=options)
|
|
519
|
+
return res.json()
|
|
520
|
+
except requests.RequestException as err:
|
|
521
|
+
self.logger.error(str(err))
|
|
522
|
+
if err.response and err.response.json().get("errors"):
|
|
523
|
+
return err.response.json()
|
|
524
|
+
return {"errors": [{"message": str(err)}]}
|
|
525
|
+
|
|
526
|
+
def get_search_schema(self, schema):
|
|
527
|
+
self.logger.debug(f"getSearchSchema [schema={json.dumps(schema)}]")
|
|
528
|
+
try:
|
|
529
|
+
fields = {}
|
|
530
|
+
for k, v in schema.items():
|
|
531
|
+
for key, val in v.items():
|
|
532
|
+
fields[f"{k.lower()}_{key.lower()}"] = {
|
|
533
|
+
"type": get_search_type(val["dataType"])
|
|
534
|
+
}
|
|
535
|
+
fields[f"{k.lower()}__label"] = {"type": "TEXT"}
|
|
536
|
+
return fields
|
|
537
|
+
except Exception as err:
|
|
538
|
+
self.logger.error(str(err))
|
|
539
|
+
return {"errors": [{"message": str(err)}]}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
from typing import Any, Dict, List, Optional
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
5
|
+
from .PromptStore import PromptStore
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class PromptTemplateOnStart(BaseModel):
|
|
9
|
+
prompt_set_name: str
|
|
10
|
+
message_templates: List[Any]
|
|
11
|
+
prompt_set_version: Optional[str] = None
|
|
12
|
+
args: Optional[Any] = None
|
|
13
|
+
is_batch: Optional[bool] = False
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class PromptTemplateEnd(BaseModel):
|
|
17
|
+
messages: Optional[List[Any]] = None
|
|
18
|
+
errors: Optional[Any] = None
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ModelStart(BaseModel):
|
|
22
|
+
provider: str
|
|
23
|
+
request: Any
|
|
24
|
+
llm_params: Any
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ModelEnd(BaseModel):
|
|
28
|
+
errors: Optional[Any]
|
|
29
|
+
response: Optional[Any]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class AgentStart(BaseModel):
|
|
33
|
+
name: str
|
|
34
|
+
model: str
|
|
35
|
+
provider: str
|
|
36
|
+
args: Optional[Any] = None
|
|
37
|
+
tools: Optional[List[Any]] = None
|
|
38
|
+
extra_function_call_params: Optional[Any] = None
|
|
39
|
+
llm_params: Optional[Any] = None
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class Trace:
|
|
43
|
+
ps: PromptStore
|
|
44
|
+
id: str
|
|
45
|
+
span_stack: List[Dict[str, Any]]
|
|
46
|
+
|
|
47
|
+
def __init__(self):
|
|
48
|
+
self.ps = PromptStore()
|
|
49
|
+
self.id = self.ps.trace({"event": {"type": "trace"}})
|
|
50
|
+
self.span_stack = []
|
|
51
|
+
|
|
52
|
+
def span(self, event: Dict[str, Any]):
|
|
53
|
+
self.ps.trace({"event": event, "id": self.id})
|
|
54
|
+
sp = Span(self, self.ps, event)
|
|
55
|
+
self.span_stack.append(sp)
|
|
56
|
+
return sp
|
|
57
|
+
|
|
58
|
+
def pop(self):
|
|
59
|
+
return self.span_stack.pop()
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class Span:
|
|
63
|
+
ps: PromptStore
|
|
64
|
+
trace: Trace
|
|
65
|
+
event: Dict[str, Any]
|
|
66
|
+
|
|
67
|
+
def __init__(self, trace: Trace, ps: PromptStore, event: Dict[str, Any]):
|
|
68
|
+
self.trace = trace
|
|
69
|
+
self.ps = ps
|
|
70
|
+
self.event = event
|
|
71
|
+
|
|
72
|
+
def end(self, event: Dict[str, Any]):
|
|
73
|
+
self.ps.trace({"event": event, "id": self.trace.id})
|
|
74
|
+
self.event = {**self.event, **event}
|
|
75
|
+
return self.trace.pop()
|
|
76
|
+
|
|
77
|
+
def span(self, event: Dict[str, Any]):
|
|
78
|
+
return self.trace.span(event)
|
|
79
|
+
|
|
80
|
+
def generation(self, event: Dict[str, Any]):
|
|
81
|
+
return self.trace.span(event)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
def is_empty(value):
|
|
2
|
+
if isinstance(value, list):
|
|
3
|
+
return len(value) == 0
|
|
4
|
+
if is_object(value):
|
|
5
|
+
return len(value.keys()) == 0
|
|
6
|
+
if isinstance(value, str):
|
|
7
|
+
return value == ''
|
|
8
|
+
return value is None
|
|
9
|
+
|
|
10
|
+
def is_object(value):
|
|
11
|
+
return value is not None and isinstance(value, (dict, object))
|
|
12
|
+
|
|
13
|
+
def strip_empty_values(obj):
|
|
14
|
+
def inner(val):
|
|
15
|
+
if isinstance(val, list):
|
|
16
|
+
if len(val):
|
|
17
|
+
arr = [inner(x) for x in val if inner(x) is not None]
|
|
18
|
+
if len(arr):
|
|
19
|
+
return arr
|
|
20
|
+
elif is_object(val):
|
|
21
|
+
obj = {k: inner(v) for k, v in val.items() if inner(v) is not None}
|
|
22
|
+
if not is_empty(obj):
|
|
23
|
+
return obj
|
|
24
|
+
else:
|
|
25
|
+
if val is not None and val != '':
|
|
26
|
+
return val
|
|
27
|
+
return None
|
|
28
|
+
|
|
29
|
+
return inner(obj)
|
|
30
|
+
|
|
31
|
+
def get_search_type(data_type):
|
|
32
|
+
if data_type in ['String', 'DateTime']:
|
|
33
|
+
return 'TEXT'
|
|
34
|
+
elif data_type == 'Boolean':
|
|
35
|
+
return 'TAG'
|
|
36
|
+
elif data_type in ['Double', 'Long']:
|
|
37
|
+
return 'NUMERIC'
|
|
38
|
+
else:
|
|
39
|
+
return 'TEXT'
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "promptstorepy"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = ""
|
|
5
|
+
authors = ["Mark Moloney <markmo@me.com>"]
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
|
|
8
|
+
[tool.poetry.dependencies]
|
|
9
|
+
python = "^3.11"
|
|
10
|
+
asyncio = "^3.4.3"
|
|
11
|
+
requests = "^2.32.2"
|
|
12
|
+
langchain-core = "^0.2.1"
|
|
13
|
+
devtools = "^0.12.2"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
[build-system]
|
|
17
|
+
requires = ["poetry-core"]
|
|
18
|
+
build-backend = "poetry.core.masonry.api"
|