Spaces:
Sleeping
Sleeping
Commit
·
516495b
0
Parent(s):
init
Browse files- .gitattributes +1 -0
- .gitignore +11 -0
- Dockerfile +17 -0
- README.md +8 -0
- idreamers/__init__.py +34 -0
- idreamers/api/calculation/__init__.py +7 -0
- idreamers/api/calculation/openai_request.py +27 -0
- idreamers/api/calculation/prompts.py +141 -0
- idreamers/api/calculation/schemas.py +12 -0
- idreamers/api/calculation/utils.py +8 -0
- idreamers/api/calculation/views.py +21 -0
- idreamers/core/config.py +36 -0
- idreamers/core/wrappers.py +57 -0
- main.py +3 -0
- requirements.txt +25 -0
.gitattributes
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
*.index filter=lfs diff=lfs merge=lfs -text
|
.gitignore
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
__pycache__/
|
2 |
+
env/
|
3 |
+
venv/
|
4 |
+
.venv/
|
5 |
+
.idea/
|
6 |
+
*.log
|
7 |
+
*.egg-info/
|
8 |
+
pip-wheel-EntityData/
|
9 |
+
.env
|
10 |
+
.DS_Store
|
11 |
+
static/
|
Dockerfile
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM python:3.12.7
|
2 |
+
|
3 |
+
RUN useradd -m -u 1000 user
|
4 |
+
USER user
|
5 |
+
ENV PATH="/home/user/.local/bin:$PATH"
|
6 |
+
|
7 |
+
WORKDIR /app
|
8 |
+
|
9 |
+
COPY --chown=user ./requirements.txt requirements.txt
|
10 |
+
RUN pip install --no-cache-dir --upgrade -r requirements.txt
|
11 |
+
|
12 |
+
USER root
|
13 |
+
RUN apt-get update && apt-get install -y poppler-utils
|
14 |
+
USER user
|
15 |
+
|
16 |
+
COPY --chown=user . /app
|
17 |
+
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
|
README.md
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
title: Supplier Segmentation
|
3 |
+
emoji: 💻
|
4 |
+
colorFrom: yellow
|
5 |
+
colorTo: blue
|
6 |
+
sdk: docker
|
7 |
+
pinned: false
|
8 |
+
---
|
idreamers/__init__.py
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import FastAPI
|
2 |
+
from fastapi.middleware.cors import CORSMiddleware
|
3 |
+
from starlette.exceptions import HTTPException as StarletteHTTPException
|
4 |
+
|
5 |
+
from idreamers.core.config import settings
|
6 |
+
from idreamers.core.wrappers import PilotResponseWrapper, ErrorPilotResponse
|
7 |
+
|
8 |
+
|
9 |
+
def create_app() -> FastAPI:
|
10 |
+
app = FastAPI()
|
11 |
+
|
12 |
+
from idreamers.api.calculation import calculation_router
|
13 |
+
app.include_router(calculation_router, tags=['calculation'])
|
14 |
+
|
15 |
+
app.add_middleware(
|
16 |
+
CORSMiddleware,
|
17 |
+
allow_origins=["*"],
|
18 |
+
allow_methods=["*"],
|
19 |
+
allow_headers=["*"],
|
20 |
+
)
|
21 |
+
|
22 |
+
@app.exception_handler(StarletteHTTPException)
|
23 |
+
async def http_exception_handler(_, exc):
|
24 |
+
return PilotResponseWrapper(
|
25 |
+
data=None,
|
26 |
+
successful=False,
|
27 |
+
error=ErrorPilotResponse(message=str(exc.detail))
|
28 |
+
).response(exc.status_code)
|
29 |
+
|
30 |
+
@app.get("/")
|
31 |
+
async def read_root():
|
32 |
+
return {"calculation": "Hello world!"}
|
33 |
+
|
34 |
+
return app
|
idreamers/api/calculation/__init__.py
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi.routing import APIRouter
|
2 |
+
|
3 |
+
calculation_router = APIRouter(
|
4 |
+
prefix="/api/calculation", tags=["calculation"]
|
5 |
+
)
|
6 |
+
|
7 |
+
from . import views
|
idreamers/api/calculation/openai_request.py
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from typing import Literal
|
2 |
+
|
3 |
+
from idreamers.api.calculation.prompts import CalculationPrompts
|
4 |
+
from idreamers.api.calculation.schemas import PilotRequest
|
5 |
+
from idreamers.core.wrappers import openai_wrapper
|
6 |
+
|
7 |
+
|
8 |
+
@openai_wrapper(is_json=True, return_='score')
|
9 |
+
async def calculate_scores(data: PilotRequest, type_: Literal["business", "competitive", "supplier", "critical"]):
|
10 |
+
if type_ == "competitive":
|
11 |
+
prompt = CalculationPrompts.competitive
|
12 |
+
elif type_ == "supplier":
|
13 |
+
prompt = CalculationPrompts.supplier
|
14 |
+
elif type_ == "business":
|
15 |
+
prompt = CalculationPrompts.business
|
16 |
+
else:
|
17 |
+
prompt = CalculationPrompts.critical
|
18 |
+
messages = [
|
19 |
+
{
|
20 |
+
"role": "system",
|
21 |
+
"content": prompt
|
22 |
+
.replace("{supplier}", data.supplier)
|
23 |
+
.replace("{category}", data.category)
|
24 |
+
.replace("{buying_company}", data.buyingCompany)
|
25 |
+
}
|
26 |
+
]
|
27 |
+
return messages
|
idreamers/api/calculation/prompts.py
ADDED
@@ -0,0 +1,141 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
class CalculationPrompts:
|
2 |
+
competitive = """## Task
|
3 |
+
You must answer on the question below and set a score.
|
4 |
+
|
5 |
+
Question:
|
6 |
+
```
|
7 |
+
How dependent is your business on this supplier for critical operations?
|
8 |
+
```
|
9 |
+
|
10 |
+
Think about whether this supplier is essential for your day-to-day operations.
|
11 |
+
|
12 |
+
## Data
|
13 |
+
|
14 |
+
**Supplier**:
|
15 |
+
```
|
16 |
+
{supplier}
|
17 |
+
```
|
18 |
+
|
19 |
+
**Category**:
|
20 |
+
```
|
21 |
+
{category}
|
22 |
+
```
|
23 |
+
|
24 |
+
**Buying Company**:
|
25 |
+
```
|
26 |
+
{buying_company}
|
27 |
+
```
|
28 |
+
|
29 |
+
## JSON Response format
|
30 |
+
|
31 |
+
```json
|
32 |
+
{
|
33 |
+
"score": integer
|
34 |
+
}
|
35 |
+
|
36 |
+
- [score]: Numerical score between 1 and 4. 1 = Many, 4 = Very few."""
|
37 |
+
supplier = """## Task
|
38 |
+
You must answer on the question below and set a score.
|
39 |
+
|
40 |
+
Question:
|
41 |
+
```
|
42 |
+
How competitive is the market where this supplier operates?
|
43 |
+
```
|
44 |
+
|
45 |
+
Evaluate whether this supplier faces competition or has significant market control.
|
46 |
+
|
47 |
+
## Data
|
48 |
+
|
49 |
+
**Supplier**:
|
50 |
+
```
|
51 |
+
{supplier}
|
52 |
+
```
|
53 |
+
|
54 |
+
**Category**:
|
55 |
+
```
|
56 |
+
{category}
|
57 |
+
```
|
58 |
+
|
59 |
+
**Buying Company**:
|
60 |
+
```
|
61 |
+
{buying_company}
|
62 |
+
```
|
63 |
+
|
64 |
+
## JSON Response format
|
65 |
+
|
66 |
+
```json
|
67 |
+
{
|
68 |
+
"score": integer
|
69 |
+
}
|
70 |
+
|
71 |
+
- [score]: Numerical score between 1 and 4. 1 = Highly competitive, 4 = Monopoly/Oligopoly."""
|
72 |
+
business = """## Task
|
73 |
+
You must answer on the question below and set a score.
|
74 |
+
|
75 |
+
Question:
|
76 |
+
```
|
77 |
+
How dependent is the supplier on your organization for their revenue?
|
78 |
+
```
|
79 |
+
|
80 |
+
Reflect on how important your business is to this supplier’s overall revenue.
|
81 |
+
|
82 |
+
## Data
|
83 |
+
|
84 |
+
**Supplier**:
|
85 |
+
```
|
86 |
+
{supplier}
|
87 |
+
```
|
88 |
+
|
89 |
+
**Category**:
|
90 |
+
```
|
91 |
+
{category}
|
92 |
+
```
|
93 |
+
|
94 |
+
**Buying Company**:
|
95 |
+
```
|
96 |
+
{buying_company}
|
97 |
+
```
|
98 |
+
|
99 |
+
## JSON Response format
|
100 |
+
|
101 |
+
```json
|
102 |
+
{
|
103 |
+
"score": integer
|
104 |
+
}
|
105 |
+
|
106 |
+
- [score]: Numerical score between 1 and 4. 1 = Not dependent, 4 = Highly dependent."""
|
107 |
+
critical = """## Task
|
108 |
+
You must answer on the question below and set a score.
|
109 |
+
|
110 |
+
Question:
|
111 |
+
```
|
112 |
+
How critical is this supplier for delivering unique or differentiated products/services?
|
113 |
+
```
|
114 |
+
|
115 |
+
Think about whether this supplier provides something that others cannot easily replicate.
|
116 |
+
|
117 |
+
## Data
|
118 |
+
|
119 |
+
**Supplier**:
|
120 |
+
```
|
121 |
+
{supplier}
|
122 |
+
```
|
123 |
+
|
124 |
+
**Category**:
|
125 |
+
```
|
126 |
+
{category}
|
127 |
+
```
|
128 |
+
|
129 |
+
**Buying Company**:
|
130 |
+
```
|
131 |
+
{buying_company}
|
132 |
+
```
|
133 |
+
|
134 |
+
## JSON Response format
|
135 |
+
|
136 |
+
```json
|
137 |
+
{
|
138 |
+
"score": integer
|
139 |
+
}
|
140 |
+
|
141 |
+
- [score]: Numerical score between 1 and 4. Think about whether this supplier provides something that others cannot easily replicate."""
|
idreamers/api/calculation/schemas.py
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from pydantic import BaseModel
|
2 |
+
|
3 |
+
|
4 |
+
class PilotRequest(BaseModel):
|
5 |
+
category: str
|
6 |
+
buyingCompany: str
|
7 |
+
supplier: str
|
8 |
+
|
9 |
+
|
10 |
+
class PilotResponse(BaseModel):
|
11 |
+
x: float
|
12 |
+
y: float
|
idreamers/api/calculation/utils.py
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from idreamers.api.calculation.schemas import PilotResponse
|
2 |
+
|
3 |
+
|
4 |
+
def calculate_pilot_axes(q1: int, q2: int, q3: int, q4: int) -> PilotResponse:
|
5 |
+
return PilotResponse(
|
6 |
+
x=round((q1 + q2) / 2, 2),
|
7 |
+
y=round((q3 + q4) / 2, 2),
|
8 |
+
)
|
idreamers/api/calculation/views.py
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import asyncio
|
2 |
+
|
3 |
+
from idreamers.api.calculation import calculation_router
|
4 |
+
from idreamers.api.calculation.openai_request import calculate_scores
|
5 |
+
from idreamers.api.calculation.schemas import PilotRequest, PilotResponse
|
6 |
+
from idreamers.api.calculation.utils import calculate_pilot_axes
|
7 |
+
from idreamers.core.wrappers import PilotResponseWrapper
|
8 |
+
|
9 |
+
|
10 |
+
@calculation_router.post('/pilot/calculate')
|
11 |
+
async def calculate_pilot(
|
12 |
+
data: PilotRequest
|
13 |
+
) -> PilotResponseWrapper[PilotResponse]:
|
14 |
+
depend_business, competitive, depend_supplier, critical_supplier = await asyncio.gather(
|
15 |
+
calculate_scores(data, 'business'),
|
16 |
+
calculate_scores(data, 'competitive'),
|
17 |
+
calculate_scores(data, 'supplier'),
|
18 |
+
calculate_scores(data, 'critical'),
|
19 |
+
)
|
20 |
+
response = calculate_pilot_axes(depend_business, competitive, depend_supplier, critical_supplier)
|
21 |
+
return PilotResponseWrapper(data=response)
|
idreamers/core/config.py
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import pathlib
|
3 |
+
from functools import lru_cache
|
4 |
+
|
5 |
+
from dotenv import load_dotenv
|
6 |
+
from openai import AsyncClient
|
7 |
+
|
8 |
+
load_dotenv()
|
9 |
+
|
10 |
+
|
11 |
+
class BaseConfig:
|
12 |
+
BASE_DIR: pathlib.Path = pathlib.Path(__file__).parent.parent.parent
|
13 |
+
SECRET_KEY = os.getenv('SECRET')
|
14 |
+
OPENAI_CLIENT = AsyncClient(api_key=os.getenv('OPENAI_API_KEY'))
|
15 |
+
|
16 |
+
|
17 |
+
class DevelopmentConfig(BaseConfig):
|
18 |
+
pass
|
19 |
+
|
20 |
+
|
21 |
+
class ProductionConfig(BaseConfig):
|
22 |
+
pass
|
23 |
+
|
24 |
+
|
25 |
+
@lru_cache()
|
26 |
+
def get_settings() -> DevelopmentConfig | ProductionConfig:
|
27 |
+
config_cls_dict = {
|
28 |
+
'development': DevelopmentConfig,
|
29 |
+
'production': ProductionConfig,
|
30 |
+
}
|
31 |
+
config_name = os.getenv('FASTAPI_CONFIG', default='development')
|
32 |
+
config_cls = config_cls_dict[config_name]
|
33 |
+
return config_cls()
|
34 |
+
|
35 |
+
|
36 |
+
settings = get_settings()
|
idreamers/core/wrappers.py
ADDED
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import json
|
2 |
+
from functools import wraps
|
3 |
+
from typing import Generic, Optional, TypeVar
|
4 |
+
|
5 |
+
import pydash
|
6 |
+
from pydantic import BaseModel
|
7 |
+
from starlette.responses import JSONResponse
|
8 |
+
|
9 |
+
from idreamers.core.config import settings
|
10 |
+
|
11 |
+
T = TypeVar('T')
|
12 |
+
|
13 |
+
|
14 |
+
class ErrorPilotResponse(BaseModel):
|
15 |
+
message: str
|
16 |
+
|
17 |
+
|
18 |
+
class PilotResponseWrapper(BaseModel, Generic[T]):
|
19 |
+
data: Optional[T] = None
|
20 |
+
successful: bool = True
|
21 |
+
error: Optional[ErrorPilotResponse] = None
|
22 |
+
|
23 |
+
def response(self, status_code: int):
|
24 |
+
return JSONResponse(
|
25 |
+
status_code=status_code,
|
26 |
+
content={
|
27 |
+
"data": self.data,
|
28 |
+
"successful": self.successful,
|
29 |
+
"error": self.error.model_dump(mode='json') if self.error else None
|
30 |
+
}
|
31 |
+
)
|
32 |
+
|
33 |
+
|
34 |
+
def openai_wrapper(
|
35 |
+
temperature: int | float = 0, model: str = "gpt-4o-mini", is_json: bool = False, return_: str = None
|
36 |
+
):
|
37 |
+
def decorator(func):
|
38 |
+
@wraps(func)
|
39 |
+
async def wrapper(*args, **kwargs) -> str:
|
40 |
+
messages = await func(*args, **kwargs)
|
41 |
+
completion = await settings.OPENAI_CLIENT.chat.completions.create(
|
42 |
+
messages=messages,
|
43 |
+
temperature=temperature,
|
44 |
+
n=1,
|
45 |
+
model=model,
|
46 |
+
response_format={"type": "json_object"} if is_json else {"type": "text"}
|
47 |
+
)
|
48 |
+
response = completion.choices[0].message.content
|
49 |
+
if is_json:
|
50 |
+
response = json.loads(response)
|
51 |
+
if return_:
|
52 |
+
return pydash.get(response, return_)
|
53 |
+
return response
|
54 |
+
|
55 |
+
return wrapper
|
56 |
+
|
57 |
+
return decorator
|
main.py
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
from idreamers import create_app
|
2 |
+
|
3 |
+
app = create_app()
|
requirements.txt
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
annotated-types==0.7.0
|
2 |
+
anyio==4.8.0
|
3 |
+
certifi==2024.12.14
|
4 |
+
click==8.1.8
|
5 |
+
distro==1.9.0
|
6 |
+
fastapi==0.115.7
|
7 |
+
h11==0.14.0
|
8 |
+
httpcore==1.0.7
|
9 |
+
httptools==0.6.4
|
10 |
+
httpx==0.28.1
|
11 |
+
idna==3.10
|
12 |
+
jiter==0.8.2
|
13 |
+
openai==1.60.1
|
14 |
+
pydantic==2.10.6
|
15 |
+
pydantic_core==2.27.2
|
16 |
+
python-dotenv==1.0.1
|
17 |
+
PyYAML==6.0.2
|
18 |
+
sniffio==1.3.1
|
19 |
+
starlette==0.45.3
|
20 |
+
tqdm==4.67.1
|
21 |
+
typing_extensions==4.12.2
|
22 |
+
uvicorn==0.34.0
|
23 |
+
uvloop==0.21.0
|
24 |
+
watchfiles==1.0.4
|
25 |
+
websockets==14.2
|