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 ADDED
@@ -0,0 +1,8 @@
1
+ from .prompt import Prompt
2
+ from .pipeline import Pipeline
3
+ from .manager import PromptManager, PipelineManager
4
+ from .server import PipelineServer
5
+ from .api import PipelineAPI
6
+ from .log import log
7
+
8
+ __version__ = "0.0.1"
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