sunholo 0.116.2__py3-none-any.whl → 0.118.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.
- sunholo/chunker/message_data.py +1 -1
- sunholo/chunker/pdfs.py +1 -1
- sunholo/chunker/publish.py +1 -1
- sunholo/cli/cli_init.py +1 -1
- sunholo/genai/__init__.py +2 -1
- sunholo/genai/genaiv2.py +542 -0
- sunholo/{types.py → langchain_types.py} +5 -5
- sunholo/langfuse/evals.py +2 -1
- sunholo/mcp/cli.py +0 -2
- sunholo/templates/agent/__init__.py +0 -0
- sunholo/templates/agent/agent_service.py +157 -0
- sunholo/templates/agent/app.py +16 -0
- sunholo/templates/agent/my_log.py +3 -0
- sunholo/templates/agent/tools/__init__.py +0 -0
- sunholo/templates/agent/tools/your_agent.py +78 -0
- sunholo/templates/agent/vac_service.py +73 -0
- sunholo/templates/project/__init__.py +0 -0
- sunholo/templates/project/app.py +17 -0
- sunholo/templates/project/my_log.py +3 -0
- sunholo/templates/project/vac_service.py +71 -0
- sunholo/templates/system_services/__init__.py +0 -0
- sunholo/templates/system_services/app.py +49 -0
- sunholo/templates/system_services/my_log.py +3 -0
- sunholo/utils/big_context.py +11 -4
- {sunholo-0.116.2.dist-info → sunholo-0.118.1.dist-info}/METADATA +15 -7
- {sunholo-0.116.2.dist-info → sunholo-0.118.1.dist-info}/RECORD +30 -15
- {sunholo-0.116.2.dist-info → sunholo-0.118.1.dist-info}/WHEEL +1 -1
- {sunholo-0.116.2.dist-info → sunholo-0.118.1.dist-info}/LICENSE.txt +0 -0
- {sunholo-0.116.2.dist-info → sunholo-0.118.1.dist-info}/entry_points.txt +0 -0
- {sunholo-0.116.2.dist-info → sunholo-0.118.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
|
|
2
|
+
from sunholo.utils import ConfigManager
|
|
3
|
+
from sunholo.vertex import (
|
|
4
|
+
init_genai,
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
from tools.your_agent import get_quarto, quarto_content, QuartoProcessor
|
|
8
|
+
|
|
9
|
+
from my_log import log
|
|
10
|
+
|
|
11
|
+
init_genai()
|
|
12
|
+
|
|
13
|
+
# kwargs supports - image_uri, mime
|
|
14
|
+
def vac_stream(question: str, vector_name:str, chat_history=[], callback=None, **kwargs):
|
|
15
|
+
|
|
16
|
+
config=ConfigManager(vector_name)
|
|
17
|
+
processor = QuartoProcessor(config)
|
|
18
|
+
|
|
19
|
+
orchestrator = get_quarto(config, processor)
|
|
20
|
+
if not orchestrator:
|
|
21
|
+
msg = f"No quarto model could be configured for {vector_name}"
|
|
22
|
+
log.error(msg)
|
|
23
|
+
callback.on_llm_end(response=msg)
|
|
24
|
+
return {"answer": msg}
|
|
25
|
+
|
|
26
|
+
chat = orchestrator.start_chat()
|
|
27
|
+
|
|
28
|
+
guardrail = 0
|
|
29
|
+
guardrail_max = kwargs.get('max_steps', 10)
|
|
30
|
+
big_text = ""
|
|
31
|
+
usage_metadata = None
|
|
32
|
+
functions_called = []
|
|
33
|
+
result=None
|
|
34
|
+
last_responses=None
|
|
35
|
+
while guardrail < guardrail_max:
|
|
36
|
+
|
|
37
|
+
content = quarto_content(question, chat_history)
|
|
38
|
+
log.info(f"# Loop [{guardrail}] - {content=}")
|
|
39
|
+
response = chat.send_message(content, stream=True)
|
|
40
|
+
this_text = "" # reset for this loop
|
|
41
|
+
log.debug(f"[{guardrail}] {response}")
|
|
42
|
+
|
|
43
|
+
for chunk in response:
|
|
44
|
+
try:
|
|
45
|
+
log.debug(f"[{guardrail}] {chunk=}")
|
|
46
|
+
# Check if 'text' is an attribute of chunk and if it's a string
|
|
47
|
+
if hasattr(chunk, 'text') and isinstance(chunk.text, str):
|
|
48
|
+
token = chunk.text
|
|
49
|
+
else:
|
|
50
|
+
function_names = []
|
|
51
|
+
try:
|
|
52
|
+
for part in chunk.candidates[0].content.parts:
|
|
53
|
+
if fn := part.function_call:
|
|
54
|
+
params = {key: val for key, val in fn.args.items()}
|
|
55
|
+
func_args = ",".join(f"{key}={value}" for key, value in params.items())
|
|
56
|
+
log.info(f"Found function call: {fn.name}({func_args})")
|
|
57
|
+
function_names.append(f"{fn.name}({func_args})")
|
|
58
|
+
functions_called.append(f"{fn.name}({func_args})")
|
|
59
|
+
except Exception as err:
|
|
60
|
+
log.warning(f"{str(err)}")
|
|
61
|
+
|
|
62
|
+
token = "" # Handle the case where 'text' is not available
|
|
63
|
+
|
|
64
|
+
if processor.last_api_requests_and_responses:
|
|
65
|
+
if processor.last_api_requests_and_responses != last_responses:
|
|
66
|
+
last_responses = processor.last_api_requests_and_responses
|
|
67
|
+
for last_response in last_responses:
|
|
68
|
+
result=None # reset for this function response
|
|
69
|
+
if last_response:
|
|
70
|
+
log.info(f"[{guardrail}] {last_response=}")
|
|
71
|
+
|
|
72
|
+
# Convert the last_response to a string by extracting relevant information
|
|
73
|
+
function_name = last_response[0]
|
|
74
|
+
arguments = last_response[1]
|
|
75
|
+
result = last_response[2]
|
|
76
|
+
func_args = ",".join(f"{key}={value}" for key, value in arguments.items())
|
|
77
|
+
|
|
78
|
+
if f"{function_name}({func_args})" not in function_names:
|
|
79
|
+
log.warning(f"skipping {function_name}({func_args}) as not in execution list")
|
|
80
|
+
continue
|
|
81
|
+
|
|
82
|
+
token = f"\n## Loop [{guardrail}] Function call: {function_name}({func_args}):\n"
|
|
83
|
+
|
|
84
|
+
if function_name == "decide_to_go_on":
|
|
85
|
+
token += f"# go_on={result}\n"
|
|
86
|
+
else:
|
|
87
|
+
log.info("Adding result for: {function_name}")
|
|
88
|
+
token += result
|
|
89
|
+
|
|
90
|
+
callback.on_llm_new_token(token=token)
|
|
91
|
+
big_text += token
|
|
92
|
+
this_text += token
|
|
93
|
+
|
|
94
|
+
if not usage_metadata:
|
|
95
|
+
chunk_metadata = chunk.usage_metadata
|
|
96
|
+
usage_metadata = {
|
|
97
|
+
"prompt_token_count": chunk_metadata.prompt_token_count,
|
|
98
|
+
"candidates_token_count": chunk_metadata.candidates_token_count,
|
|
99
|
+
"total_token_count": chunk_metadata.total_token_count,
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
except ValueError as err:
|
|
103
|
+
callback.on_llm_new_token(token=str(err))
|
|
104
|
+
|
|
105
|
+
# change response to one with executed functions
|
|
106
|
+
response = processor.process_funcs(response)
|
|
107
|
+
|
|
108
|
+
if this_text:
|
|
109
|
+
chat_history.append(("<waiting for ai>", this_text))
|
|
110
|
+
log.info(f"[{guardrail}] Updated chat_history: {chat_history}")
|
|
111
|
+
|
|
112
|
+
go_on_check = processor.check_function_result("decide_to_go_on", False)
|
|
113
|
+
if go_on_check:
|
|
114
|
+
log.info("Breaking agent loop")
|
|
115
|
+
break
|
|
116
|
+
|
|
117
|
+
guardrail += 1
|
|
118
|
+
if guardrail > guardrail_max:
|
|
119
|
+
log.warning("Guardrail kicked in, more than 10 loops")
|
|
120
|
+
break
|
|
121
|
+
|
|
122
|
+
callback.on_llm_end(response=big_text)
|
|
123
|
+
log.info(f"orchestrator.response: {big_text}")
|
|
124
|
+
|
|
125
|
+
metadata = {
|
|
126
|
+
"question:": question,
|
|
127
|
+
"chat_history": chat_history,
|
|
128
|
+
"usage_metadata": usage_metadata,
|
|
129
|
+
"functions_called": functions_called
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return {"answer": big_text or "No answer was given", "metadata": metadata}
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def vac(question: str, vector_name: str, chat_history=[], **kwargs):
|
|
136
|
+
# Create a callback that does nothing for streaming if you don't want intermediate outputs
|
|
137
|
+
class NoOpCallback:
|
|
138
|
+
def on_llm_new_token(self, token):
|
|
139
|
+
pass
|
|
140
|
+
def on_llm_end(self, response):
|
|
141
|
+
pass
|
|
142
|
+
|
|
143
|
+
# Use the NoOpCallback for non-streaming behavior
|
|
144
|
+
callback = NoOpCallback()
|
|
145
|
+
|
|
146
|
+
# Pass all arguments to vac_stream and use the final return
|
|
147
|
+
result = vac_stream(
|
|
148
|
+
question=question,
|
|
149
|
+
vector_name=vector_name,
|
|
150
|
+
chat_history=chat_history,
|
|
151
|
+
callback=callback,
|
|
152
|
+
**kwargs
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
return result
|
|
156
|
+
|
|
157
|
+
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from sunholo.agents import VACRoutes, create_app
|
|
4
|
+
|
|
5
|
+
from vac_service import vac_stream, vac
|
|
6
|
+
|
|
7
|
+
app = create_app(__name__)
|
|
8
|
+
|
|
9
|
+
# Register the Q&A routes with the specific interpreter functions
|
|
10
|
+
# creates /vac/<vector_name> and /vac/streaming/<vector_name>
|
|
11
|
+
VACRoutes(app, vac_stream, vac)
|
|
12
|
+
|
|
13
|
+
if __name__ == "__main__":
|
|
14
|
+
import os
|
|
15
|
+
app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 8080)), debug=True)
|
|
16
|
+
|
|
File without changes
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
from sunholo.genai import GenAIFunctionProcessor
|
|
2
|
+
from sunholo.utils import ConfigManager
|
|
3
|
+
|
|
4
|
+
from my_log import log
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class QuartoProcessor(GenAIFunctionProcessor):
|
|
8
|
+
def construct_tools(self) -> dict:
|
|
9
|
+
tools = self.config.vacConfig("tools")
|
|
10
|
+
quarto_config = tools.get("quarto")
|
|
11
|
+
|
|
12
|
+
def decide_to_go_on(go_on: bool):
|
|
13
|
+
"""
|
|
14
|
+
Examine the chat history. If the answer to the user's question has been answered, then go_on=False.
|
|
15
|
+
If the chat history indicates the answer is still being looked for, then go_on=True.
|
|
16
|
+
If there is no chat history, then go_on=True.
|
|
17
|
+
If there is an error that can't be corrected or solved by you, then go_on=False.
|
|
18
|
+
If there is an error but you think you can solve it by correcting your function arguments (such as an incorrect source), then go_on=True
|
|
19
|
+
If you want to ask the user a question or for some more feedback, then go_on=False.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
go_on: boolean Whether to continue searching or fetching from the AlloyDB database
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
boolean: True to carry on, False to continue
|
|
26
|
+
"""
|
|
27
|
+
return go_on
|
|
28
|
+
|
|
29
|
+
def quarto_render() -> dict:
|
|
30
|
+
"""
|
|
31
|
+
...
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
|
|
38
|
+
"""
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
"quarto_render": quarto_render,
|
|
43
|
+
"decide_to_go_on": decide_to_go_on
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
def quarto_content(question: str, chat_history=[]) -> str:
|
|
47
|
+
prompt_config = ConfigManager("quarto")
|
|
48
|
+
alloydb_template = prompt_config.promptConfig("quarto_template")
|
|
49
|
+
|
|
50
|
+
conversation_text = ""
|
|
51
|
+
for human, ai in chat_history:
|
|
52
|
+
conversation_text += f"Human: {human}\nAI: {ai}\n"
|
|
53
|
+
|
|
54
|
+
return alloydb_template.format(the_question=question, chat_history=conversation_text[-10000:])
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def get_quarto(config:ConfigManager, processor:QuartoProcessor):
|
|
58
|
+
|
|
59
|
+
tools = config.vacConfig('tools')
|
|
60
|
+
|
|
61
|
+
if tools and tools.get('quarto'):
|
|
62
|
+
model_name = None
|
|
63
|
+
if config.vacConfig('llm') != "vertex":
|
|
64
|
+
model_name = 'gemini-1.5-flash'
|
|
65
|
+
alloydb_model = processor.get_model(
|
|
66
|
+
system_instruction=(
|
|
67
|
+
"You are a helpful Quarto agent that helps users create and render Quarto documents. "
|
|
68
|
+
"When you think the answer has been given to the satisfaction of the user, or you think no answer is possible, or you need user confirmation or input, you MUST use the decide_to_go_on(go_on=False) function"
|
|
69
|
+
"When you want to ask the question to the user, mark the go_on=False in the function"
|
|
70
|
+
),
|
|
71
|
+
model_name=model_name
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
if alloydb_model:
|
|
75
|
+
return alloydb_model
|
|
76
|
+
|
|
77
|
+
log.error("Error initializing quarto model")
|
|
78
|
+
return None
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
from my_log import log
|
|
2
|
+
from sunholo.utils import ConfigManager
|
|
3
|
+
|
|
4
|
+
# VAC specific imports
|
|
5
|
+
|
|
6
|
+
#TODO: Developer to update to their own implementation
|
|
7
|
+
from sunholo.vertex import init_vertex, get_vertex_memories
|
|
8
|
+
from vertexai.preview.generative_models import GenerativeModel
|
|
9
|
+
|
|
10
|
+
#TODO: change this to a streaming VAC function
|
|
11
|
+
def vac_stream(question: str, vector_name, chat_history=[], callback=None, **kwargs):
|
|
12
|
+
|
|
13
|
+
rag_model = create_model(vector_name)
|
|
14
|
+
|
|
15
|
+
# streaming model calls
|
|
16
|
+
response = rag_model.generate_content(question, stream=True)
|
|
17
|
+
for chunk in response:
|
|
18
|
+
try:
|
|
19
|
+
callback.on_llm_new_token(token=chunk.text)
|
|
20
|
+
except ValueError as err:
|
|
21
|
+
callback.on_llm_new_token(token=str(err))
|
|
22
|
+
|
|
23
|
+
callback.on_llm_end(response=response)
|
|
24
|
+
log.info(f"rag_model.response: {response}")
|
|
25
|
+
|
|
26
|
+
metadata = {
|
|
27
|
+
"chat_history": chat_history
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return {"answer": response.text, "metadata": metadata}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
#TODO: change this to a batch VAC function
|
|
35
|
+
def vac(question: str, vector_name: str, chat_history=[], **kwargs):
|
|
36
|
+
# Create a callback that does nothing for streaming if you don't want intermediate outputs
|
|
37
|
+
class NoOpCallback:
|
|
38
|
+
def on_llm_new_token(self, token):
|
|
39
|
+
pass
|
|
40
|
+
def on_llm_end(self, response):
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
# Use the NoOpCallback for non-streaming behavior
|
|
44
|
+
callback = NoOpCallback()
|
|
45
|
+
|
|
46
|
+
# Pass all arguments to vac_stream and use the final return
|
|
47
|
+
result = vac_stream(
|
|
48
|
+
question=question,
|
|
49
|
+
vector_name=vector_name,
|
|
50
|
+
chat_history=chat_history,
|
|
51
|
+
callback=callback,
|
|
52
|
+
**kwargs
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
return result
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# TODO: common model setup to both batching and streaming
|
|
59
|
+
def create_model(vac):
|
|
60
|
+
config = ConfigManager(vac)
|
|
61
|
+
|
|
62
|
+
init_vertex()
|
|
63
|
+
corpus_tools = get_vertex_memories(config)
|
|
64
|
+
|
|
65
|
+
model = config.vacConfig("model")
|
|
66
|
+
|
|
67
|
+
# Create a gemini-pro model instance
|
|
68
|
+
# https://ai.google.dev/api/python/google/generativeai/GenerativeModel#streaming
|
|
69
|
+
rag_model = GenerativeModel(
|
|
70
|
+
model_name=model or "gemini-1.5-flash", tools=[corpus_tools]
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
return rag_model
|
|
File without changes
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from sunholo.agents import VACRoutes, create_app
|
|
4
|
+
|
|
5
|
+
from vac_service import vac_stream
|
|
6
|
+
|
|
7
|
+
app = create_app(__name__)
|
|
8
|
+
|
|
9
|
+
# Register the Q&A routes with the specific interpreter functions
|
|
10
|
+
# creates endpoints /vac/streaming/<vector_name> and /vac/<vector_name> etc.
|
|
11
|
+
VACRoutes(app, vac_stream)
|
|
12
|
+
|
|
13
|
+
# start via `python app.py`
|
|
14
|
+
if __name__ == "__main__":
|
|
15
|
+
import os
|
|
16
|
+
app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 8080)), debug=True)
|
|
17
|
+
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from my_log import log
|
|
2
|
+
from sunholo.utils import ConfigManager
|
|
3
|
+
|
|
4
|
+
# VAC specific imports
|
|
5
|
+
|
|
6
|
+
#TODO: Developer to update to their own implementation
|
|
7
|
+
from sunholo.genai import init_genai, genai_safety
|
|
8
|
+
import google.generativeai as genai
|
|
9
|
+
|
|
10
|
+
#TODO: change this to a streaming VAC function for your use case
|
|
11
|
+
def vac_stream(question: str, vector_name:str, chat_history=[], callback=None, **kwargs):
|
|
12
|
+
|
|
13
|
+
model = create_model(vector_name)
|
|
14
|
+
|
|
15
|
+
# create chat history for genai model
|
|
16
|
+
# https://ai.google.dev/api/generate-content
|
|
17
|
+
contents = []
|
|
18
|
+
for human, ai in chat_history:
|
|
19
|
+
if human:
|
|
20
|
+
contents.append({"role":"user", "parts":[{"text": human}]})
|
|
21
|
+
|
|
22
|
+
if ai:
|
|
23
|
+
contents.append({"role":"model", "parts":[{"text": ai}]})
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# the user question at the end of contents list
|
|
27
|
+
contents.append({"role":"user", "parts":[{"text": question}]})
|
|
28
|
+
|
|
29
|
+
log.info(contents)
|
|
30
|
+
# streaming model calls
|
|
31
|
+
response = model.generate_content(contents, stream=True)
|
|
32
|
+
chunks=""
|
|
33
|
+
for chunk in response:
|
|
34
|
+
if chunk and chunk.text:
|
|
35
|
+
try:
|
|
36
|
+
callback.on_llm_new_token(token=chunk.text)
|
|
37
|
+
chunks += chunk.text
|
|
38
|
+
except ValueError as err:
|
|
39
|
+
callback.on_llm_new_token(token=str(err))
|
|
40
|
+
|
|
41
|
+
# stream has finished, full response is also returned
|
|
42
|
+
callback.on_llm_end(response=response)
|
|
43
|
+
log.info(f"model.response: {response}")
|
|
44
|
+
|
|
45
|
+
metadata = {
|
|
46
|
+
"question": question,
|
|
47
|
+
"vector_name": vector_name,
|
|
48
|
+
"chat_history": chat_history
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
# to not return this dict at the end of the stream, pass stream_only: true in request
|
|
52
|
+
return {"answer": chunks, "metadata": metadata}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
# TODO: example model setup function
|
|
56
|
+
def create_model(vac):
|
|
57
|
+
config = ConfigManager(vac)
|
|
58
|
+
|
|
59
|
+
init_genai()
|
|
60
|
+
|
|
61
|
+
# get a setting from the config vacConfig object (returns None if not found)
|
|
62
|
+
model = config.vacConfig("model")
|
|
63
|
+
|
|
64
|
+
# Create a gemini-flash model instance
|
|
65
|
+
# https://ai.google.dev/api/python/google/generativeai/GenerativeModel#streaming
|
|
66
|
+
genai_model = genai.GenerativeModel(
|
|
67
|
+
model_name=model or "gemini-1.5-flash",
|
|
68
|
+
safety_settings=genai_safety()
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
return genai_model
|
|
File without changes
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import traceback
|
|
3
|
+
|
|
4
|
+
# app.py
|
|
5
|
+
from fastapi import FastAPI, Request
|
|
6
|
+
from fastapi.responses import JSONResponse
|
|
7
|
+
|
|
8
|
+
from my_log import log
|
|
9
|
+
|
|
10
|
+
app = FastAPI()
|
|
11
|
+
|
|
12
|
+
@app.get("/")
|
|
13
|
+
def home():
|
|
14
|
+
"""Simple endpoint to indicate that the app is running."""
|
|
15
|
+
return {"message": "Hello, service!"}
|
|
16
|
+
|
|
17
|
+
@app.post("/system_service/<param>")
|
|
18
|
+
async def system_service(request: Request):
|
|
19
|
+
"""
|
|
20
|
+
Pubsub message parsed and sent to Langfuse ID server
|
|
21
|
+
"""
|
|
22
|
+
data = await request.json()
|
|
23
|
+
|
|
24
|
+
try:
|
|
25
|
+
#TODO: add stuff here
|
|
26
|
+
meta = ""
|
|
27
|
+
return {"status": "success", "message": meta}
|
|
28
|
+
except Exception as err:
|
|
29
|
+
log.error(f'EVAL_ERROR: Error when sending {data} to /pubsub_to_langfuse: {str(err)} traceback: {traceback.format_exc()}')
|
|
30
|
+
return JSONResponse(status_code=200, content={"status": "error", "message": f'{str(err)} traceback: {traceback.format_exc()}'})
|
|
31
|
+
|
|
32
|
+
@app.post("/test_endpoint")
|
|
33
|
+
async def test_me(request: Request):
|
|
34
|
+
"""
|
|
35
|
+
Endpoint to send trace_ids directly for evals then sent to Langfuse ID server
|
|
36
|
+
"""
|
|
37
|
+
data = await request.json()
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
#TODO: do something here
|
|
41
|
+
meta = ""
|
|
42
|
+
return {"status": "success", "message": meta}
|
|
43
|
+
except Exception as err:
|
|
44
|
+
log.error(f'EVAL_ERROR: Error when sending {data} to /direct_evals: {str(err)} traceback: {traceback.format_exc()}')
|
|
45
|
+
return JSONResponse(status_code=500, content={"status": "error", "message": f'{str(err)} traceback: {traceback.format_exc()}'})
|
|
46
|
+
|
|
47
|
+
if __name__ == "__main__":
|
|
48
|
+
import uvicorn
|
|
49
|
+
uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT", 8080)), debug=True)
|
sunholo/utils/big_context.py
CHANGED
|
@@ -43,7 +43,7 @@ def load_gitignore_patterns(gitignore_path):
|
|
|
43
43
|
"""
|
|
44
44
|
with open(gitignore_path, 'r') as f:
|
|
45
45
|
patterns = [line.strip() for line in f if line.strip() and not line.startswith('#')]
|
|
46
|
-
patterns.extend(["
|
|
46
|
+
patterns.extend([".git/", ".terraform/"]) # More precise pattern matching
|
|
47
47
|
return patterns
|
|
48
48
|
|
|
49
49
|
def should_ignore(file_path, patterns):
|
|
@@ -62,11 +62,18 @@ def should_ignore(file_path, patterns):
|
|
|
62
62
|
True
|
|
63
63
|
"""
|
|
64
64
|
rel_path = os.path.relpath(file_path)
|
|
65
|
-
|
|
65
|
+
|
|
66
66
|
for pattern in patterns:
|
|
67
|
-
|
|
67
|
+
# Handle directory patterns ending with /
|
|
68
|
+
if pattern.endswith('/'):
|
|
69
|
+
if any(part == pattern[:-1] for part in rel_path.split(os.sep)):
|
|
70
|
+
print(f"Ignoring {rel_path}")
|
|
71
|
+
return True
|
|
72
|
+
# Handle file patterns
|
|
73
|
+
elif fnmatch(rel_path, pattern):
|
|
74
|
+
print(f"Ignoring {rel_path}")
|
|
68
75
|
return True
|
|
69
|
-
|
|
76
|
+
|
|
70
77
|
return False
|
|
71
78
|
|
|
72
79
|
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: sunholo
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.118.1
|
|
4
4
|
Summary: Large Language Model DevOps - a package to help deploy LLMs to the Cloud.
|
|
5
|
-
|
|
6
|
-
Download-URL: https://github.com/sunholo-data/sunholo-py/archive/refs/tags/v0.116.2.tar.gz
|
|
7
|
-
Author: Holosun ApS
|
|
8
|
-
Author-email: multivac@sunholo.com
|
|
5
|
+
Author-email: Holosun ApS <multivac@sunholo.com>
|
|
9
6
|
License: Apache License, Version 2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/sunholo-data/sunholo-py
|
|
8
|
+
Project-URL: Download, https://github.com/sunholo-data/sunholo-py/archive/refs/tags/v0.118.0.tar.gz
|
|
10
9
|
Keywords: llms,devops,google_cloud_platform
|
|
11
10
|
Classifier: Development Status :: 3 - Alpha
|
|
12
11
|
Classifier: Intended Audience :: Developers
|
|
@@ -16,6 +15,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
16
15
|
Classifier: Programming Language :: Python :: 3.10
|
|
17
16
|
Classifier: Programming Language :: Python :: 3.11
|
|
18
17
|
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
19
|
Description-Content-Type: text/markdown
|
|
20
20
|
License-File: LICENSE.txt
|
|
21
21
|
Requires-Dist: aiohttp
|
|
@@ -24,6 +24,9 @@ Requires-Dist: pydantic
|
|
|
24
24
|
Requires-Dist: requests
|
|
25
25
|
Requires-Dist: ruamel.yaml
|
|
26
26
|
Requires-Dist: tenacity
|
|
27
|
+
Provides-Extra: test
|
|
28
|
+
Requires-Dist: pytest; extra == "test"
|
|
29
|
+
Requires-Dist: pytest-cov; extra == "test"
|
|
27
30
|
Provides-Extra: all
|
|
28
31
|
Requires-Dist: aiohttp; extra == "all"
|
|
29
32
|
Requires-Dist: anthropic[vertex]; extra == "all"
|
|
@@ -47,6 +50,7 @@ Requires-Dist: google-cloud-pubsub; extra == "all"
|
|
|
47
50
|
Requires-Dist: google-cloud-discoveryengine; extra == "all"
|
|
48
51
|
Requires-Dist: google-cloud-texttospeech; extra == "all"
|
|
49
52
|
Requires-Dist: google-generativeai>=0.7.1; extra == "all"
|
|
53
|
+
Requires-Dist: google-genai; extra == "all"
|
|
50
54
|
Requires-Dist: gunicorn; extra == "all"
|
|
51
55
|
Requires-Dist: httpcore; extra == "all"
|
|
52
56
|
Requires-Dist: httpx; extra == "all"
|
|
@@ -64,6 +68,7 @@ Requires-Dist: langchain-unstructured; extra == "all"
|
|
|
64
68
|
Requires-Dist: langfuse; extra == "all"
|
|
65
69
|
Requires-Dist: mcp; extra == "all"
|
|
66
70
|
Requires-Dist: numpy; extra == "all"
|
|
71
|
+
Requires-Dist: opencv-python; extra == "all"
|
|
67
72
|
Requires-Dist: pg8000; extra == "all"
|
|
68
73
|
Requires-Dist: pgvector; extra == "all"
|
|
69
74
|
Requires-Dist: pillow; extra == "all"
|
|
@@ -118,9 +123,9 @@ Requires-Dist: unstructured[all-docs,local-inference]; extra == "pipeline"
|
|
|
118
123
|
Provides-Extra: gcp
|
|
119
124
|
Requires-Dist: anthropic[vertex]; extra == "gcp"
|
|
120
125
|
Requires-Dist: google-api-python-client; extra == "gcp"
|
|
121
|
-
Requires-Dist: google-cloud-alloydb-connector[pg8000]; extra == "gcp"
|
|
122
126
|
Requires-Dist: google-auth-httplib2; extra == "gcp"
|
|
123
127
|
Requires-Dist: google-auth-oauthlib; extra == "gcp"
|
|
128
|
+
Requires-Dist: google-cloud-alloydb-connector[pg8000]; extra == "gcp"
|
|
124
129
|
Requires-Dist: google-cloud-aiplatform>=1.58.0; extra == "gcp"
|
|
125
130
|
Requires-Dist: google-cloud-bigquery; extra == "gcp"
|
|
126
131
|
Requires-Dist: google-cloud-build; extra == "gcp"
|
|
@@ -130,6 +135,7 @@ Requires-Dist: google-cloud-logging; extra == "gcp"
|
|
|
130
135
|
Requires-Dist: google-cloud-pubsub; extra == "gcp"
|
|
131
136
|
Requires-Dist: google-cloud-discoveryengine; extra == "gcp"
|
|
132
137
|
Requires-Dist: google-cloud-texttospeech; extra == "gcp"
|
|
138
|
+
Requires-Dist: google-genai; extra == "gcp"
|
|
133
139
|
Requires-Dist: google-generativeai>=0.8.3; extra == "gcp"
|
|
134
140
|
Requires-Dist: langchain-google-genai>=2.0.0; extra == "gcp"
|
|
135
141
|
Requires-Dist: langchain_google_alloydb_pg>=0.2.2; extra == "gcp"
|
|
@@ -164,6 +170,8 @@ Provides-Extra: tts
|
|
|
164
170
|
Requires-Dist: google-cloud-texttospeech; extra == "tts"
|
|
165
171
|
Requires-Dist: numpy; extra == "tts"
|
|
166
172
|
Requires-Dist: sounddevice; extra == "tts"
|
|
173
|
+
Provides-Extra: video
|
|
174
|
+
Requires-Dist: opencv-python; extra == "video"
|
|
167
175
|
|
|
168
176
|
[](https://pypi.python.org/pypi/sunholo/)
|
|
169
177
|
|