sigmaflow 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.
- sigmaflow/__init__.py +8 -0
- sigmaflow/api.py +86 -0
- sigmaflow/clients/__init__.py +22 -0
- sigmaflow/clients/llm_lmdeploy.py +87 -0
- sigmaflow/clients/llm_mlx.py +24 -0
- sigmaflow/clients/llm_mlx_batch.py +63 -0
- sigmaflow/clients/llm_ollama.py +53 -0
- sigmaflow/clients/llm_openai.py +59 -0
- sigmaflow/clients/llm_pulse.py +115 -0
- sigmaflow/clients/llm_torch.py +50 -0
- sigmaflow/clients/llm_vllm.py +72 -0
- sigmaflow/clients/rag_http.py +98 -0
- sigmaflow/clients/rag_json.py +19 -0
- sigmaflow/clients/search_bing.py +54 -0
- sigmaflow/log.py +32 -0
- sigmaflow/manager.py +124 -0
- sigmaflow/pipe.py +270 -0
- sigmaflow/pipeline.py +249 -0
- sigmaflow/pipetree.py +1315 -0
- sigmaflow/prompt.py +59 -0
- sigmaflow/server.py +384 -0
- sigmaflow/utils.py +58 -0
- sigmaflow-0.0.1.dist-info/METADATA +276 -0
- sigmaflow-0.0.1.dist-info/RECORD +27 -0
- sigmaflow-0.0.1.dist-info/WHEEL +5 -0
- sigmaflow-0.0.1.dist-info/licenses/LICENSE +201 -0
- sigmaflow-0.0.1.dist-info/top_level.txt +1 -0
sigmaflow/__init__.py
ADDED
sigmaflow/api.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from fastapi import APIRouter
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
from fastapi import HTTPException
|
|
5
|
+
from . import PromptManager, PipelineManager
|
|
6
|
+
|
|
7
|
+
class PromptData(BaseModel):
|
|
8
|
+
name: str | None = None
|
|
9
|
+
text: str | None = None
|
|
10
|
+
keys: list | None = None
|
|
11
|
+
|
|
12
|
+
class PipeData(BaseModel):
|
|
13
|
+
name: str | None = None
|
|
14
|
+
data: dict | None = None
|
|
15
|
+
|
|
16
|
+
class PipelineAPI:
|
|
17
|
+
def __init__(self, prompt_dir, pipeline_dir, llm_client=None, rag_client=None, is_async=False):
|
|
18
|
+
router = APIRouter(prefix='/api')
|
|
19
|
+
prompt_manager = PromptManager(prompt_dir)
|
|
20
|
+
pipeline_manager = PipelineManager(llm_client, rag_client, is_async, pipeline_dir, prompt_manager)
|
|
21
|
+
|
|
22
|
+
@router.get("/prompt/list")
|
|
23
|
+
async def prompt_list():
|
|
24
|
+
try:
|
|
25
|
+
ret = {k: {'text': v.text, 'keys': v.keys} for k,v in prompt_manager.prompts.items()}
|
|
26
|
+
return ret
|
|
27
|
+
|
|
28
|
+
except Exception as e:
|
|
29
|
+
raise HTTPException(status_code=500, detail=str(e))
|
|
30
|
+
|
|
31
|
+
@router.post("/prompt/update")
|
|
32
|
+
async def prompt_update(p_data: PromptData):
|
|
33
|
+
try:
|
|
34
|
+
if p_data.text:
|
|
35
|
+
prompt_manager.prompts[p_data.name].text = p_data.text
|
|
36
|
+
if p_data.keys:
|
|
37
|
+
prompt_manager.prompts[p_data.name].keys = p_data.keys
|
|
38
|
+
|
|
39
|
+
ret = {
|
|
40
|
+
p_data.name: {
|
|
41
|
+
'text': prompt_manager.prompts[p_data.name].text,
|
|
42
|
+
'keys': prompt_manager.prompts[p_data.name].keys
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return ret
|
|
46
|
+
|
|
47
|
+
except Exception as e:
|
|
48
|
+
raise HTTPException(status_code=500, detail=str(e))
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@router.get("/pipeline/list")
|
|
52
|
+
async def pipeline_list():
|
|
53
|
+
try:
|
|
54
|
+
ret = pipeline_manager.export_pipe_conf()
|
|
55
|
+
return ret
|
|
56
|
+
|
|
57
|
+
except Exception as e:
|
|
58
|
+
raise HTTPException(status_code=500, detail=str(e))
|
|
59
|
+
|
|
60
|
+
@router.post("/pipeline/mermaid")
|
|
61
|
+
async def pipeline_mermaid(p_data: PipeData):
|
|
62
|
+
try:
|
|
63
|
+
pipeline = pipeline_manager.pipes[p_data.name]['func']
|
|
64
|
+
mermaid = pipeline.pipe2mermaid(pipeline.pipe)
|
|
65
|
+
ret = {
|
|
66
|
+
'mermaid': mermaid,
|
|
67
|
+
}
|
|
68
|
+
return ret
|
|
69
|
+
|
|
70
|
+
except Exception as e:
|
|
71
|
+
raise HTTPException(status_code=500, detail=str(e))
|
|
72
|
+
|
|
73
|
+
@router.post("/pipeline/update")
|
|
74
|
+
async def pipeline_update(p_data: PipeData):
|
|
75
|
+
try:
|
|
76
|
+
ret = {
|
|
77
|
+
'result': pipeline_manager.update_pipe(p_data.name, p_data.data)
|
|
78
|
+
}
|
|
79
|
+
return ret
|
|
80
|
+
|
|
81
|
+
except Exception as e:
|
|
82
|
+
raise HTTPException(status_code=500, detail=str(e))
|
|
83
|
+
|
|
84
|
+
self.router = router
|
|
85
|
+
self.prompt_manager = prompt_manager
|
|
86
|
+
self.pipeline_manager = pipeline_manager
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import asyncio
|
|
3
|
+
import pkgutil
|
|
4
|
+
import importlib
|
|
5
|
+
from ..log import log
|
|
6
|
+
|
|
7
|
+
class _SearchEngine:
|
|
8
|
+
def __init__(self):
|
|
9
|
+
log.debug('Setup SearchEngine')
|
|
10
|
+
self.engines = {}
|
|
11
|
+
for loader, module_name, is_pkg in pkgutil.walk_packages(__path__, __name__ + '.'):
|
|
12
|
+
if (name := module_name.split('.')[-1]).startswith('search_'):
|
|
13
|
+
name = name.replace('search_', '')
|
|
14
|
+
m = importlib.import_module(module_name)
|
|
15
|
+
self.engines[name] = [m.search, m.async_search]
|
|
16
|
+
log.debug(f'Load engines: {list(self.engines.keys())}')
|
|
17
|
+
|
|
18
|
+
def get(self, engine_name):
|
|
19
|
+
return self.engines[engine_name]
|
|
20
|
+
|
|
21
|
+
SearchEngine = _SearchEngine()
|
|
22
|
+
llm_sem = asyncio.Semaphore(int(os.getenv('LLM_CONCURRENCY', 16)))
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
from . import *
|
|
2
|
+
spec = importlib.util.find_spec('lmdeploy')
|
|
3
|
+
|
|
4
|
+
if spec:
|
|
5
|
+
from lmdeploy import pipeline, GenerationConfig, TurbomindEngineConfig, ChatTemplateConfig
|
|
6
|
+
|
|
7
|
+
model_path = os.getenv('LMDEPLOY_MODEL')
|
|
8
|
+
chat_temp_name = os.getenv('LMDEPLOY_CHAT_TEMP', None)
|
|
9
|
+
session_len = int(os.getenv('LMDEPLOY_SESSION_LEN', 256))
|
|
10
|
+
max_new_tokens = int(os.getenv('LMDEPLOY_MAX_NEW_TOKENS', 256))
|
|
11
|
+
batch_size = int(os.getenv('LMDEPLOY_BATCH_SIZE', 128))
|
|
12
|
+
pp = int(os.getenv('LMDEPLOY_PP', 1))
|
|
13
|
+
tp = int(os.getenv('LMDEPLOY_TP', 1))
|
|
14
|
+
|
|
15
|
+
backend_config = TurbomindEngineConfig(
|
|
16
|
+
session_len=session_len,
|
|
17
|
+
pp=pp,
|
|
18
|
+
tp=tp)
|
|
19
|
+
gen_config = GenerationConfig(
|
|
20
|
+
do_sample=True,
|
|
21
|
+
# top_p=0.95,
|
|
22
|
+
# temperature=0.7,
|
|
23
|
+
max_new_tokens=max_new_tokens)
|
|
24
|
+
chat_temp = ChatTemplateConfig(model_name=chat_temp_name) if chat_temp_name else None
|
|
25
|
+
llm = pipeline(model_path, backend_config=backend_config, chat_template_config=chat_temp)
|
|
26
|
+
|
|
27
|
+
batch_wait_time = 1
|
|
28
|
+
batch_queue = []
|
|
29
|
+
batch_event = asyncio.Event()
|
|
30
|
+
|
|
31
|
+
def llm_client(is_async=False):
|
|
32
|
+
if is_async:
|
|
33
|
+
return async_completion
|
|
34
|
+
else:
|
|
35
|
+
return completion
|
|
36
|
+
|
|
37
|
+
def completion(text):
|
|
38
|
+
raise NotImplementedError
|
|
39
|
+
|
|
40
|
+
def batch_completion(batch_data):
|
|
41
|
+
prompts = []
|
|
42
|
+
for text, _ in batch_data:
|
|
43
|
+
if (t := type(text)) is str:
|
|
44
|
+
prompts.append(text)
|
|
45
|
+
elif t is list:
|
|
46
|
+
msg = []
|
|
47
|
+
for role, c in zip(['user', 'assistant']*len(text), text):
|
|
48
|
+
msg.append({
|
|
49
|
+
"role": role,
|
|
50
|
+
"content": c,
|
|
51
|
+
})
|
|
52
|
+
prompts.append(msg)
|
|
53
|
+
|
|
54
|
+
outputs = llm(prompts, gen_config=gen_config)
|
|
55
|
+
result = [o.text for o in outputs]
|
|
56
|
+
return result
|
|
57
|
+
|
|
58
|
+
async def llm_batch_processor():
|
|
59
|
+
global batch_queue
|
|
60
|
+
while True:
|
|
61
|
+
await batch_event.wait()
|
|
62
|
+
|
|
63
|
+
if batch_queue:
|
|
64
|
+
batch = batch_queue[:batch_size]
|
|
65
|
+
batch_queue = batch_queue[len(batch):]
|
|
66
|
+
result = await asyncio.to_thread(batch_completion, batch)
|
|
67
|
+
|
|
68
|
+
for (_, future), res in zip(batch, result):
|
|
69
|
+
future.set_result(res)
|
|
70
|
+
|
|
71
|
+
if not batch_queue:
|
|
72
|
+
batch_event.clear()
|
|
73
|
+
|
|
74
|
+
async def async_completion(text):
|
|
75
|
+
global batch_queue
|
|
76
|
+
|
|
77
|
+
loop = asyncio.get_event_loop()
|
|
78
|
+
future = loop.create_future()
|
|
79
|
+
batch_queue.append((text, future))
|
|
80
|
+
|
|
81
|
+
if len(batch_queue) >= batch_size:
|
|
82
|
+
batch_event.set()
|
|
83
|
+
else:
|
|
84
|
+
await asyncio.sleep(batch_wait_time)
|
|
85
|
+
batch_event.set()
|
|
86
|
+
|
|
87
|
+
return await future
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from mlx_lm import load, generate
|
|
3
|
+
from . import *
|
|
4
|
+
|
|
5
|
+
model, tokenizer = load(os.getenv('MLX_MODEL'))
|
|
6
|
+
max_tokens = int(os.getenv('MLX_MAX_TOKENS', 256))
|
|
7
|
+
|
|
8
|
+
def llm_client(is_async=False):
|
|
9
|
+
if is_async:
|
|
10
|
+
return async_completion
|
|
11
|
+
else:
|
|
12
|
+
return completion
|
|
13
|
+
|
|
14
|
+
def completion(text):
|
|
15
|
+
messages = [{"role": "user", "content": text}]
|
|
16
|
+
prompt = tokenizer.apply_chat_template(
|
|
17
|
+
messages, tokenize=False, add_generation_prompt=True
|
|
18
|
+
)
|
|
19
|
+
text = generate(model, tokenizer, prompt=prompt, verbose=False, max_tokens=max_tokens)
|
|
20
|
+
|
|
21
|
+
return text
|
|
22
|
+
|
|
23
|
+
async def async_completion(text):
|
|
24
|
+
return completion(text)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import os, sys
|
|
2
|
+
sys.path.append('/Users/mkk/workspace/git_repos/mlx_parallm')
|
|
3
|
+
from mlx_parallm.utils import load, batch_generate
|
|
4
|
+
from . import *
|
|
5
|
+
|
|
6
|
+
model, tokenizer = load(os.getenv('MLX_MODEL'))
|
|
7
|
+
max_tokens = int(os.getenv('MLX_MAX_TOKENS', 256))
|
|
8
|
+
batch_size = int(os.getenv('MLX_BATCH_SIZE', 128))
|
|
9
|
+
|
|
10
|
+
batch_wait_time = 1
|
|
11
|
+
batch_queue = []
|
|
12
|
+
batch_event = asyncio.Event()
|
|
13
|
+
|
|
14
|
+
def llm_client(is_async=False):
|
|
15
|
+
if is_async:
|
|
16
|
+
return async_completion
|
|
17
|
+
else:
|
|
18
|
+
return completion
|
|
19
|
+
|
|
20
|
+
def completion(text):
|
|
21
|
+
raise NotImplementedError
|
|
22
|
+
|
|
23
|
+
def batch_completion(batch_data):
|
|
24
|
+
print(f"Processed batch, len: {len(batch_data)}")
|
|
25
|
+
prompts = [text for text,_ in batch_data]
|
|
26
|
+
result = batch_generate(model, tokenizer, prompts=prompts, verbose=True, format_prompts=True, max_tokens=max_tokens, temp=0.7)
|
|
27
|
+
return result
|
|
28
|
+
|
|
29
|
+
async def llm_batch_processor():
|
|
30
|
+
global batch_queue
|
|
31
|
+
while True:
|
|
32
|
+
await batch_event.wait()
|
|
33
|
+
print("Batch processor triggered.")
|
|
34
|
+
|
|
35
|
+
if batch_queue:
|
|
36
|
+
batch = batch_queue[:batch_size]
|
|
37
|
+
batch_queue = batch_queue[len(batch):]
|
|
38
|
+
result = await asyncio.to_thread(batch_completion, batch)
|
|
39
|
+
|
|
40
|
+
for (_, future), res in zip(batch, result):
|
|
41
|
+
future.set_result(res)
|
|
42
|
+
|
|
43
|
+
if not batch_queue:
|
|
44
|
+
print("Batch queue is empty. Resetting event.")
|
|
45
|
+
batch_event.clear()
|
|
46
|
+
|
|
47
|
+
async def async_completion(text):
|
|
48
|
+
global batch_queue
|
|
49
|
+
|
|
50
|
+
loop = asyncio.get_event_loop()
|
|
51
|
+
future = loop.create_future()
|
|
52
|
+
batch_queue.append((text, future))
|
|
53
|
+
print("Task added to batch queue.")
|
|
54
|
+
|
|
55
|
+
if len(batch_queue) >= batch_size:
|
|
56
|
+
print("Batch size reached. Triggering batch processing.")
|
|
57
|
+
batch_event.set()
|
|
58
|
+
else:
|
|
59
|
+
print(f"Task waiting for batch timeout or size threshold.")
|
|
60
|
+
await asyncio.sleep(batch_wait_time)
|
|
61
|
+
batch_event.set()
|
|
62
|
+
|
|
63
|
+
return await future
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from ollama import Client, AsyncClient
|
|
2
|
+
from . import *
|
|
3
|
+
|
|
4
|
+
ollama_model = os.getenv('OLLAMA_MODEL')
|
|
5
|
+
ollama_num_ctx = int(os.getenv('OLLAMA_NUM_CTX', 2048))
|
|
6
|
+
|
|
7
|
+
def llm_client(is_async=False):
|
|
8
|
+
if is_async:
|
|
9
|
+
return async_completion
|
|
10
|
+
else:
|
|
11
|
+
return completion
|
|
12
|
+
|
|
13
|
+
def completion(text):
|
|
14
|
+
client = Client(timeout=6000)
|
|
15
|
+
resp = client.chat(model=ollama_model, messages=[
|
|
16
|
+
{
|
|
17
|
+
'role': 'user',
|
|
18
|
+
'content': text,
|
|
19
|
+
},
|
|
20
|
+
], options={
|
|
21
|
+
"num_ctx": ollama_num_ctx
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
return resp.message.content
|
|
25
|
+
|
|
26
|
+
async def async_completion(text):
|
|
27
|
+
client = AsyncClient(timeout=6000)
|
|
28
|
+
|
|
29
|
+
if (t := type(text)) is str:
|
|
30
|
+
msg = [
|
|
31
|
+
{
|
|
32
|
+
"role": "user",
|
|
33
|
+
"content": text,
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
elif t is list:
|
|
37
|
+
msg = []
|
|
38
|
+
for role, c in zip(['user', 'assistant']*len(text), text):
|
|
39
|
+
msg.append({
|
|
40
|
+
"role": role,
|
|
41
|
+
"content": c,
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
async with llm_sem:
|
|
45
|
+
resp = await client.chat(
|
|
46
|
+
model=ollama_model,
|
|
47
|
+
messages=msg,
|
|
48
|
+
options={
|
|
49
|
+
"num_ctx": ollama_num_ctx
|
|
50
|
+
}
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
return resp.message.content
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from openai import OpenAI
|
|
2
|
+
from openai import AsyncOpenAI
|
|
3
|
+
from . import *
|
|
4
|
+
|
|
5
|
+
def llm_client(is_async=False):
|
|
6
|
+
if is_async:
|
|
7
|
+
return async_completion
|
|
8
|
+
else:
|
|
9
|
+
return completion
|
|
10
|
+
|
|
11
|
+
def completion(text):
|
|
12
|
+
client = OpenAI(
|
|
13
|
+
api_key=os.getenv("OPENAI_API_KEY"),
|
|
14
|
+
base_url=os.getenv("OPENAI_API_URL"),
|
|
15
|
+
max_retries=20,
|
|
16
|
+
# timeout=6000.0,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
chat_completion = client.chat.completions.create(
|
|
20
|
+
messages=[
|
|
21
|
+
{
|
|
22
|
+
"role": "user",
|
|
23
|
+
"content": text,
|
|
24
|
+
}
|
|
25
|
+
],
|
|
26
|
+
model=os.getenv("OPENAI_API_MODEL")
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
return chat_completion.choices[0].message.content
|
|
30
|
+
|
|
31
|
+
async def async_completion(text):
|
|
32
|
+
client = AsyncOpenAI(
|
|
33
|
+
api_key=os.getenv("OPENAI_API_KEY"),
|
|
34
|
+
base_url=os.getenv("OPENAI_API_URL"),
|
|
35
|
+
max_retries=20,
|
|
36
|
+
# timeout=6000.0,
|
|
37
|
+
)
|
|
38
|
+
if (t := type(text)) is str:
|
|
39
|
+
msg = [
|
|
40
|
+
{
|
|
41
|
+
"role": "user",
|
|
42
|
+
"content": text,
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
elif t is list:
|
|
46
|
+
msg = []
|
|
47
|
+
for role, c in zip(['user', 'assistant']*len(text), text):
|
|
48
|
+
msg.append({
|
|
49
|
+
"role": role,
|
|
50
|
+
"content": c,
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
async with llm_sem:
|
|
54
|
+
chat_completion = await client.chat.completions.create(
|
|
55
|
+
messages=msg,
|
|
56
|
+
model=os.getenv("OPENAI_API_MODEL")
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
return chat_completion.choices[0].message.content
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
from . import *
|
|
2
|
+
import json
|
|
3
|
+
import httpx
|
|
4
|
+
from fastapi import HTTPException
|
|
5
|
+
|
|
6
|
+
pulse_model = os.getenv('PULSE_MODEL')
|
|
7
|
+
pulse_token = os.getenv('PULSE_TOKEN')
|
|
8
|
+
pulse_url = os.getenv('PULSE_URL')
|
|
9
|
+
max_tokens = int(os.getenv('PULSE_MAX_TOKENS', 2048))
|
|
10
|
+
retry_count = int(os.getenv('PULSE_RETRY_COUNT', 5))
|
|
11
|
+
repetition_penalty = float(os.getenv('PULSE_REPETITION_PENALTY', 1))
|
|
12
|
+
temperature = float(os.getenv('PULSE_TEMPERATURE', 0.7))
|
|
13
|
+
top_p = float(os.getenv('PULSE_TOP_P', 0.1))
|
|
14
|
+
top_k = int(os.getenv('PULSE_TOP_K', 10))
|
|
15
|
+
|
|
16
|
+
def llm_client(is_async=False):
|
|
17
|
+
if is_async:
|
|
18
|
+
return async_completion
|
|
19
|
+
else:
|
|
20
|
+
return completion
|
|
21
|
+
|
|
22
|
+
def completion(text):
|
|
23
|
+
payload = json.dumps({
|
|
24
|
+
"action": "To user",
|
|
25
|
+
"parent_messages": [
|
|
26
|
+
{
|
|
27
|
+
"action": "From user",
|
|
28
|
+
"content": text
|
|
29
|
+
}
|
|
30
|
+
],
|
|
31
|
+
"gen_kwargs": {
|
|
32
|
+
"model": pulse_model,
|
|
33
|
+
"num_return_sequences": 1,
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
headers = {
|
|
37
|
+
'accept': 'application/json',
|
|
38
|
+
'Authorization': f'Bearer {pulse_token}',
|
|
39
|
+
'Content-Type': 'application/json'
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
response = requests.request("POST", pulse_url, headers=headers, data=payload, verify=False)
|
|
43
|
+
if response.status_code != 200:
|
|
44
|
+
raise Exception(f"Failed to generate completion: {response.text}")
|
|
45
|
+
|
|
46
|
+
return response.json()['messages'][0]['content']['parts'][0]
|
|
47
|
+
|
|
48
|
+
async def async_completion(text):
|
|
49
|
+
messages = [
|
|
50
|
+
{
|
|
51
|
+
'action': 'From user', # 'To user'
|
|
52
|
+
'content': text,
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
async with llm_sem:
|
|
57
|
+
input_data = {
|
|
58
|
+
"action": "To user",
|
|
59
|
+
"parent_messages": messages,
|
|
60
|
+
"gen_kwargs": {
|
|
61
|
+
"model": pulse_model,
|
|
62
|
+
"num_return_sequences": 1,
|
|
63
|
+
"temperature": temperature,
|
|
64
|
+
"top_p": top_p,
|
|
65
|
+
"top_k": top_k,
|
|
66
|
+
"max_tokens": max_tokens,
|
|
67
|
+
"repetition_penalty": repetition_penalty,
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return await _get_completion(
|
|
72
|
+
input_data=input_data,
|
|
73
|
+
retry=retry_count
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
async def _get_completion(input_data: dict, retry: int = 5):
|
|
77
|
+
try:
|
|
78
|
+
async with httpx.AsyncClient(verify=False) as client:
|
|
79
|
+
async with client.stream(
|
|
80
|
+
"POST",
|
|
81
|
+
pulse_url,
|
|
82
|
+
json=input_data,
|
|
83
|
+
headers={
|
|
84
|
+
"Authorization": f"Bearer {pulse_token}",
|
|
85
|
+
},
|
|
86
|
+
timeout=600,
|
|
87
|
+
) as stream:
|
|
88
|
+
# error
|
|
89
|
+
if stream.status_code != 200:
|
|
90
|
+
error_detail = ""
|
|
91
|
+
try:
|
|
92
|
+
error_detail = (await stream.aread()).decode("utf8")
|
|
93
|
+
error_detail = str(json.loads(error_detail))
|
|
94
|
+
except:
|
|
95
|
+
pass
|
|
96
|
+
|
|
97
|
+
raise HTTPException(status_code=500, detail=error_detail)
|
|
98
|
+
|
|
99
|
+
content = (await stream.aread()).decode("utf8")
|
|
100
|
+
content = json.loads(content)
|
|
101
|
+
content = "".join(content['messages'][0]['content']['parts'])
|
|
102
|
+
return content
|
|
103
|
+
except Exception as e:
|
|
104
|
+
# logger.error(json.dumps({
|
|
105
|
+
# "retry": retry,
|
|
106
|
+
# "input": input_data,
|
|
107
|
+
# "error": traceback.format_exc()
|
|
108
|
+
# }, ensure_ascii=False, indent=4))
|
|
109
|
+
# 重试次数到达极限
|
|
110
|
+
if retry == 0: raise e
|
|
111
|
+
|
|
112
|
+
return await _get_completion(
|
|
113
|
+
input_data=input_data,
|
|
114
|
+
retry=retry-1,
|
|
115
|
+
)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from . import *
|
|
2
|
+
import torch
|
|
3
|
+
from transformers import pipeline
|
|
4
|
+
|
|
5
|
+
model_path = os.getenv('MODEL_PATH')
|
|
6
|
+
max_new_tokens = int(os.getenv('MAX_TOKENS', 256))
|
|
7
|
+
|
|
8
|
+
pipe = pipeline(
|
|
9
|
+
"text-generation",
|
|
10
|
+
model=model_path,
|
|
11
|
+
torch_dtype=torch.bfloat16,
|
|
12
|
+
device_map="auto"
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
def llm_client(is_async=False):
|
|
16
|
+
if is_async:
|
|
17
|
+
return async_completion
|
|
18
|
+
else:
|
|
19
|
+
return completion
|
|
20
|
+
|
|
21
|
+
def completion(text):
|
|
22
|
+
if (t := type(text)) is str:
|
|
23
|
+
msg = [
|
|
24
|
+
{
|
|
25
|
+
"role": "user",
|
|
26
|
+
"content": text,
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
elif t is list:
|
|
30
|
+
msg = []
|
|
31
|
+
for role, c in zip(['user', 'assistant']*len(text), text):
|
|
32
|
+
msg.append({
|
|
33
|
+
"role": role,
|
|
34
|
+
"content": c,
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
resp = pipe(
|
|
38
|
+
msg,
|
|
39
|
+
do_sample=True,
|
|
40
|
+
max_new_tokens=max_new_tokens,
|
|
41
|
+
pad_token_id = pipe.tokenizer.eos_token_id
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
return resp[0]["generated_text"][-1]['content']
|
|
45
|
+
|
|
46
|
+
async def async_completion(text):
|
|
47
|
+
async with llm_sem:
|
|
48
|
+
resp = completion(text)
|
|
49
|
+
|
|
50
|
+
return resp
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
from . import *
|
|
2
|
+
spec = importlib.util.find_spec('vllm')
|
|
3
|
+
|
|
4
|
+
if spec:
|
|
5
|
+
from vllm import LLM, SamplingParams
|
|
6
|
+
|
|
7
|
+
model_path = os.getenv('VLLM_MODEL')
|
|
8
|
+
max_model_len = int(os.getenv('VLLM_MAX_MODEL_LEN', 256))
|
|
9
|
+
max_tokens = int(os.getenv('VLLM_MAX_TOKENS', 256))
|
|
10
|
+
batch_size = int(os.getenv('VLLM_BATCH_SIZE', 128))
|
|
11
|
+
pp = int(os.getenv('VLLM_PP', 1))
|
|
12
|
+
tp = int(os.getenv('VLLM_TP', 1))
|
|
13
|
+
|
|
14
|
+
llm = LLM(model=model_path,
|
|
15
|
+
gpu_memory_utilization=.95,
|
|
16
|
+
max_model_len=max_model_len,
|
|
17
|
+
pipeline_parallel_size=pp,
|
|
18
|
+
tensor_parallel_size=tp)
|
|
19
|
+
sampling_params = SamplingParams(temperature=0.7,
|
|
20
|
+
max_tokens=max_tokens,
|
|
21
|
+
min_p=0.15,
|
|
22
|
+
top_p=0.85)
|
|
23
|
+
|
|
24
|
+
batch_wait_time = 1
|
|
25
|
+
batch_queue = []
|
|
26
|
+
batch_event = asyncio.Event()
|
|
27
|
+
|
|
28
|
+
def llm_client(is_async=False):
|
|
29
|
+
if is_async:
|
|
30
|
+
return async_completion
|
|
31
|
+
else:
|
|
32
|
+
return completion
|
|
33
|
+
|
|
34
|
+
def completion(text):
|
|
35
|
+
raise NotImplementedError
|
|
36
|
+
|
|
37
|
+
def batch_completion(batch_data):
|
|
38
|
+
prompts = [text for text,_ in batch_data]
|
|
39
|
+
outputs = llm.generate(prompts, sampling_params)
|
|
40
|
+
result = [o.outputs[0].text for o in outputs]
|
|
41
|
+
return result
|
|
42
|
+
|
|
43
|
+
async def llm_batch_processor():
|
|
44
|
+
global batch_queue
|
|
45
|
+
while True:
|
|
46
|
+
await batch_event.wait()
|
|
47
|
+
|
|
48
|
+
if batch_queue:
|
|
49
|
+
batch = batch_queue[:batch_size]
|
|
50
|
+
batch_queue = batch_queue[len(batch):]
|
|
51
|
+
result = await asyncio.to_thread(batch_completion, batch)
|
|
52
|
+
|
|
53
|
+
for (_, future), res in zip(batch, result):
|
|
54
|
+
future.set_result(res)
|
|
55
|
+
|
|
56
|
+
if not batch_queue:
|
|
57
|
+
batch_event.clear()
|
|
58
|
+
|
|
59
|
+
async def async_completion(text):
|
|
60
|
+
global batch_queue
|
|
61
|
+
|
|
62
|
+
loop = asyncio.get_event_loop()
|
|
63
|
+
future = loop.create_future()
|
|
64
|
+
batch_queue.append((text, future))
|
|
65
|
+
|
|
66
|
+
if len(batch_queue) >= batch_size:
|
|
67
|
+
batch_event.set()
|
|
68
|
+
else:
|
|
69
|
+
await asyncio.sleep(batch_wait_time)
|
|
70
|
+
batch_event.set()
|
|
71
|
+
|
|
72
|
+
return await future
|