adjusted users
Browse files- README +80 -0
- __pycache__/app.cpython-310.pyc +0 -0
- __pycache__/crud.cpython-310.pyc +0 -0
- __pycache__/database.cpython-310.pyc +0 -0
- __pycache__/main.cpython-310.pyc +0 -0
- __pycache__/models.cpython-310.pyc +0 -0
- __pycache__/schemas.cpython-310.pyc +0 -0
- app.db +0 -0
- app.py +6 -9
- app/__init__.py +0 -0
- app/__pycache__/__init__.cpython-310.pyc +0 -0
- app/__pycache__/app.cpython-310.pyc +0 -0
- app/__pycache__/auth.cpython-310.pyc +0 -0
- app/__pycache__/config.cpython-310.pyc +0 -0
- app/__pycache__/gtts.cpython-310.pyc +0 -0
- app/__pycache__/main.cpython-310.pyc +0 -0
- app/__pycache__/security.cpython-310.pyc +0 -0
- app/auth.py +0 -40
- app/config.py +0 -7
- app/db/__pycache__/database.cpython-310.pyc +0 -0
- app/db/database.py +0 -19
- app/fapp.py +0 -62
- app/gtts.py +0 -119
- app/hello.py +0 -7
- app/models/__pycache__/user.cpython-310.pyc +0 -0
- app/models/user.py +0 -9
- app/routers/__pycache__/llm.cpython-310.pyc +0 -0
- app/routers/__pycache__/user.cpython-310.pyc +0 -0
- app/routers/llm.py +0 -136
- app/routers/user.py +0 -38
- app/schemas/__pycache__/user.cpython-310.pyc +0 -0
- app/schemas/user.py +0 -17
- app/security.py +0 -9
- crud.py +37 -0
- database.py +13 -0
- main.py +59 -22
- models.py +26 -0
- requirements.txt +0 -0
- schemas.py +36 -0
- sql_app.db +0 -0
README
ADDED
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# FastAPI Databases
|
2 |
+
|
3 |
+
Create a basic FastAPI application with a SQLAlchemy database backend. This can be used to access and update a client database with the option to add new clients, items, or transactions.
|
4 |
+
|
5 |
+
The project is structured with the following components:
|
6 |
+
|
7 |
+
```
|
8 |
+
my_super_project/
|
9 |
+
│
|
10 |
+
└── sql_app/
|
11 |
+
├── __init__.py
|
12 |
+
├── crud.py
|
13 |
+
├── database.py
|
14 |
+
├── main.py
|
15 |
+
├── models.py
|
16 |
+
└── schemas.py
|
17 |
+
```
|
18 |
+
|
19 |
+
## Getting Started
|
20 |
+
|
21 |
+
First, you need to install SQLAlchemy, a powerful SQL toolkit and Object-Relational Mapping (ORM) library for Python. You can do this by running the following command:
|
22 |
+
|
23 |
+
```bash
|
24 |
+
pip install sqlalchemy
|
25 |
+
```
|
26 |
+
|
27 |
+
### Database Configuration
|
28 |
+
|
29 |
+
In the `/database.py` file, you can configure your database connection. The example provided uses SQLite, but you can easily change the connection URL to your preferred database system (e.g., PostgreSQL, MySQL).
|
30 |
+
|
31 |
+
|
32 |
+
### Creating a Database Session
|
33 |
+
|
34 |
+
In the same file, a `SessionLocal` class is created, which represents a database session. This class should be used to interact with the database. The provided `Base` class is used for creating SQLAlchemy models.
|
35 |
+
|
36 |
+
### SQLAlchemy Models
|
37 |
+
|
38 |
+
In the `/models.py` file, you'll define your database models. For example, the project includes two models: `User` and `Item`. You can customize these models or add more models as needed.
|
39 |
+
|
40 |
+
You can create your own models and define their attributes as needed.
|
41 |
+
|
42 |
+
### Pydantic Models
|
43 |
+
|
44 |
+
In the `/schemas.py` file, you'll find Pydantic models that define the request and response data structures for your API. For instance, you have `UserBase`, `UserCreate`, `User`, `ItemBase`, `ItemCreate`, and `Item` Pydantic models.
|
45 |
+
|
46 |
+
These Pydantic models define the structure of data being sent to and received from your API endpoints.
|
47 |
+
|
48 |
+
### CRUD Operations
|
49 |
+
|
50 |
+
The `sql_app/crud.py` file contains utility functions for performing CRUD (Create, Read, Update, Delete) operations on the database. You can see functions to get users, create users, and get items, among others. You can modify or extend these functions to suit your needs.
|
51 |
+
|
52 |
+
### Main FastAPI Application
|
53 |
+
|
54 |
+
The `/main.py` file is where the FastAPI application is defined. It integrates all the components mentioned above, creating API endpoints to perform operations on the database.
|
55 |
+
|
56 |
+
This is just a simple example of a FastAPI application with SQLAlchemy integration. You can extend this application by adding more routes, CRUD operations, and custom business logic as per your project requirements.
|
57 |
+
|
58 |
+
## Running the Application
|
59 |
+
|
60 |
+
To run the FastAPI application, navigate to the directory and use the `uvicorn` command:
|
61 |
+
|
62 |
+
```bash
|
63 |
+
uvicorn main:app --reload
|
64 |
+
```
|
65 |
+
|
66 |
+
This will start the FastAPI development server, and you can access your API at http://localhost:8000.
|
67 |
+
|
68 |
+
## API Documentation
|
69 |
+
|
70 |
+
FastAPI automatically generates interactive API documentation for your application. You can access it at http://localhost:8000/docs, and you'll be able to test your API endpoints from there.
|
71 |
+
|
72 |
+
## Conclusion
|
73 |
+
|
74 |
+
This project is a basic example of a FastAPI application with SQLAlchemy integration, showcasing how to structure your project and perform common database operations. You can extend it to build more complex and feature-rich applications.
|
75 |
+
|
76 |
+
Contact Information
|
77 |
+
For questions, please contact [email protected].
|
78 |
+
|
79 |
+
Acknowledgments
|
80 |
+
We acknowledge the use of third-party libraries and frameworks that have contributed to the success of this project. Thank you to the open-source community for their invaluable contributions.
|
__pycache__/app.cpython-310.pyc
ADDED
Binary file (3.38 kB). View file
|
|
__pycache__/crud.cpython-310.pyc
ADDED
Binary file (1.54 kB). View file
|
|
__pycache__/database.cpython-310.pyc
ADDED
Binary file (500 Bytes). View file
|
|
__pycache__/main.cpython-310.pyc
CHANGED
Binary files a/__pycache__/main.cpython-310.pyc and b/__pycache__/main.cpython-310.pyc differ
|
|
__pycache__/models.cpython-310.pyc
ADDED
Binary file (1.06 kB). View file
|
|
__pycache__/schemas.cpython-310.pyc
ADDED
Binary file (1.56 kB). View file
|
|
app.db
DELETED
File without changes
|
app.py
CHANGED
@@ -7,7 +7,7 @@ import logging
|
|
7 |
import llama_cpp
|
8 |
import llama_cpp.llama_tokenizer
|
9 |
from pydantic import BaseModel
|
10 |
-
|
11 |
|
12 |
class GenModel(BaseModel):
|
13 |
question: str
|
@@ -63,17 +63,18 @@ app.add_middleware(
|
|
63 |
allow_headers=["*"]
|
64 |
)
|
65 |
"""
|
66 |
-
|
|
|
67 |
def index():
|
68 |
return fastapi.responses.RedirectResponse(url="/docs")
|
69 |
|
70 |
|
71 |
-
@
|
72 |
def health():
|
73 |
return {"status": "ok"}
|
74 |
|
75 |
# Chat Completion API
|
76 |
-
@
|
77 |
async def chat(chatm:ChatModel):
|
78 |
try:
|
79 |
st = time()
|
@@ -96,7 +97,7 @@ async def chat(chatm:ChatModel):
|
|
96 |
)
|
97 |
|
98 |
# Chat Completion API
|
99 |
-
@
|
100 |
async def generate(gen:GenModel):
|
101 |
gen.system = "You are an helpful medical AI assistant."
|
102 |
gen.temperature = 0.5
|
@@ -132,7 +133,3 @@ async def generate(gen:GenModel):
|
|
132 |
)
|
133 |
|
134 |
|
135 |
-
|
136 |
-
if __name__ == "__main__":
|
137 |
-
import uvicorn
|
138 |
-
uvicorn.run(app, host="0.0.0.0", port=7860)
|
|
|
7 |
import llama_cpp
|
8 |
import llama_cpp.llama_tokenizer
|
9 |
from pydantic import BaseModel
|
10 |
+
from fastapi import APIRouter
|
11 |
|
12 |
class GenModel(BaseModel):
|
13 |
question: str
|
|
|
63 |
allow_headers=["*"]
|
64 |
)
|
65 |
"""
|
66 |
+
llm_router = APIRouter()
|
67 |
+
@llm_router.get("/")
|
68 |
def index():
|
69 |
return fastapi.responses.RedirectResponse(url="/docs")
|
70 |
|
71 |
|
72 |
+
@llm_router.get("/health")
|
73 |
def health():
|
74 |
return {"status": "ok"}
|
75 |
|
76 |
# Chat Completion API
|
77 |
+
@llm_router.post("/chat/")
|
78 |
async def chat(chatm:ChatModel):
|
79 |
try:
|
80 |
st = time()
|
|
|
97 |
)
|
98 |
|
99 |
# Chat Completion API
|
100 |
+
@llm_router.post("/generate")
|
101 |
async def generate(gen:GenModel):
|
102 |
gen.system = "You are an helpful medical AI assistant."
|
103 |
gen.temperature = 0.5
|
|
|
133 |
)
|
134 |
|
135 |
|
|
|
|
|
|
|
|
app/__init__.py
DELETED
File without changes
|
app/__pycache__/__init__.cpython-310.pyc
DELETED
Binary file (139 Bytes)
|
|
app/__pycache__/app.cpython-310.pyc
DELETED
Binary file (1.68 kB)
|
|
app/__pycache__/auth.cpython-310.pyc
DELETED
Binary file (1.68 kB)
|
|
app/__pycache__/config.cpython-310.pyc
DELETED
Binary file (491 Bytes)
|
|
app/__pycache__/gtts.cpython-310.pyc
DELETED
Binary file (2.36 kB)
|
|
app/__pycache__/main.cpython-310.pyc
DELETED
Binary file (329 Bytes)
|
|
app/__pycache__/security.cpython-310.pyc
DELETED
Binary file (582 Bytes)
|
|
app/auth.py
DELETED
@@ -1,40 +0,0 @@
|
|
1 |
-
from datetime import datetime, timedelta
|
2 |
-
from typing import Optional
|
3 |
-
|
4 |
-
import jwt
|
5 |
-
from fastapi import HTTPException, Depends, status
|
6 |
-
from fastapi.security import OAuth2PasswordBearer
|
7 |
-
from sqlalchemy.orm import Session
|
8 |
-
from app.db.database import get_db
|
9 |
-
from app.models.user import User
|
10 |
-
from app.schemas.user import UserOut
|
11 |
-
|
12 |
-
SECRET_KEY = "supersecretkey"
|
13 |
-
ALGORITHM = "HS256"
|
14 |
-
ACCESS_TOKEN_EXPIRE_MINUTES = 30
|
15 |
-
|
16 |
-
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
|
17 |
-
|
18 |
-
def create_access_token(data: dict):
|
19 |
-
to_encode = data.copy()
|
20 |
-
expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
|
21 |
-
to_encode.update({"exp": expire})
|
22 |
-
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
|
23 |
-
return encoded_jwt
|
24 |
-
|
25 |
-
def verify_access_token(token: str, db: Session):
|
26 |
-
try:
|
27 |
-
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
|
28 |
-
user_id: str = payload.get("sub")
|
29 |
-
if user_id is None:
|
30 |
-
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid credentials")
|
31 |
-
user = db.query(User).filter(User.id == user_id).first()
|
32 |
-
if user is None:
|
33 |
-
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED,
|
34 |
-
detail="User not found")
|
35 |
-
return user
|
36 |
-
except jwt.PyJWTError:
|
37 |
-
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid credentials")
|
38 |
-
|
39 |
-
def get_current_user(token: str = Depends(oauth2_scheme), db: Session = Depends(get_db)):
|
40 |
-
return verify_access_token(token, db)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/config.py
DELETED
@@ -1,7 +0,0 @@
|
|
1 |
-
#from pydantic import BaseSettings
|
2 |
-
from pydantic_settings import BaseSettings
|
3 |
-
class Settings(BaseSettings):
|
4 |
-
SECRET_KEY: str = "your_secret_key"
|
5 |
-
DATABASE_URL: str = "sqlite:///./app.db"
|
6 |
-
|
7 |
-
settings = Settings()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/db/__pycache__/database.cpython-310.pyc
DELETED
Binary file (783 Bytes)
|
|
app/db/database.py
DELETED
@@ -1,19 +0,0 @@
|
|
1 |
-
from sqlalchemy import create_engine
|
2 |
-
from sqlalchemy.orm import sessionmaker, declarative_base
|
3 |
-
from app.config import settings
|
4 |
-
|
5 |
-
DATABASE_URL = settings.DATABASE_URL
|
6 |
-
|
7 |
-
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
|
8 |
-
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
9 |
-
Base = declarative_base()
|
10 |
-
|
11 |
-
def create_tables():
|
12 |
-
Base.metadata.create_all(bind=engine)
|
13 |
-
|
14 |
-
def get_db():
|
15 |
-
db = SessionLocal()
|
16 |
-
try:
|
17 |
-
yield db
|
18 |
-
finally:
|
19 |
-
db.close()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/fapp.py
DELETED
@@ -1,62 +0,0 @@
|
|
1 |
-
import os
|
2 |
-
import urllib.request
|
3 |
-
from llama_cpp import Llama
|
4 |
-
from fastapi import FastAPI
|
5 |
-
|
6 |
-
app = FastAPI(docs_url="/")
|
7 |
-
|
8 |
-
def download_file(file_link, filename):
|
9 |
-
# Checks if the file already exists before downloading
|
10 |
-
if not os.path.isfile(filename):
|
11 |
-
urllib.request.urlretrieve(file_link, filename)
|
12 |
-
print("File downloaded successfully.")
|
13 |
-
else:
|
14 |
-
print("File already exists.")
|
15 |
-
|
16 |
-
|
17 |
-
# Dowloading GGML model from HuggingFace
|
18 |
-
ggml_model_path = "https://huggingface.co/TheBloke/zephyr-7B-beta-GGUF/resolve/main/zephyr-7b-beta.Q4_0.gguf"
|
19 |
-
filename = "zephyr-7b-beta.Q4_0.gguf"
|
20 |
-
|
21 |
-
#download_file(ggml_model_path, filename)
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
llm = Llama(model_path="/home/mo/Desktop/web/oGBackend/qwen1_5-0_5b-chat-q2_k.gguf", n_ctx=512, n_batch=126, chat_format="llama")
|
26 |
-
|
27 |
-
|
28 |
-
def generate_text(
|
29 |
-
prompt="Who is the COlor of Apple?",
|
30 |
-
max_tokens=256,
|
31 |
-
temperature=0.7,
|
32 |
-
top_p=0.5,
|
33 |
-
echo=False,
|
34 |
-
stop=["#"],
|
35 |
-
):
|
36 |
-
output = llm(
|
37 |
-
prompt,
|
38 |
-
max_tokens=max_tokens,
|
39 |
-
temperature=temperature,
|
40 |
-
top_p=top_p,
|
41 |
-
echo=echo,
|
42 |
-
stop=stop,
|
43 |
-
)
|
44 |
-
output_text = output["choices"][0]["text"]
|
45 |
-
return output_text
|
46 |
-
|
47 |
-
|
48 |
-
def generate_prompt_from_template(input):
|
49 |
-
chat_prompt_template = f"""<|im_start|>system
|
50 |
-
You are a helpful chatbot.<|im_end|>
|
51 |
-
<|im_start|>user
|
52 |
-
{input}<|im_end|>"""
|
53 |
-
return chat_prompt_template
|
54 |
-
|
55 |
-
@app.get("/generate")
|
56 |
-
def generate(text: str):
|
57 |
-
prompt = generate_prompt_from_template(text)
|
58 |
-
|
59 |
-
generate_text(
|
60 |
-
prompt,
|
61 |
-
max_tokens=356,
|
62 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/gtts.py
DELETED
@@ -1,119 +0,0 @@
|
|
1 |
-
import random
|
2 |
-
import time
|
3 |
-
|
4 |
-
import speech_recognition as sr
|
5 |
-
|
6 |
-
|
7 |
-
def recognize_speech_from_mic(recognizer, microphone):
|
8 |
-
"""Transcribe speech from recorded from `microphone`.
|
9 |
-
|
10 |
-
Returns a dictionary with three keys:
|
11 |
-
"success": a boolean indicating whether or not the API request was
|
12 |
-
successful
|
13 |
-
"error": `None` if no error occured, otherwise a string containing
|
14 |
-
an error message if the API could not be reached or
|
15 |
-
speech was unrecognizable
|
16 |
-
"transcription": `None` if speech could not be transcribed,
|
17 |
-
otherwise a string containing the transcribed text
|
18 |
-
"""
|
19 |
-
# check that recognizer and microphone arguments are appropriate type
|
20 |
-
if not isinstance(recognizer, sr.Recognizer):
|
21 |
-
raise TypeError("`recognizer` must be `Recognizer` instance")
|
22 |
-
|
23 |
-
if not isinstance(microphone, sr.Microphone):
|
24 |
-
raise TypeError("`microphone` must be `Microphone` instance")
|
25 |
-
|
26 |
-
# adjust the recognizer sensitivity to ambient noise and record audio
|
27 |
-
# from the microphone
|
28 |
-
with microphone as source:
|
29 |
-
recognizer.adjust_for_ambient_noise(source)
|
30 |
-
audio = recognizer.listen(source)
|
31 |
-
|
32 |
-
# set up the response object
|
33 |
-
response = {
|
34 |
-
"success": True,
|
35 |
-
"error": None,
|
36 |
-
"transcription": None
|
37 |
-
}
|
38 |
-
|
39 |
-
# try recognizing the speech in the recording
|
40 |
-
# if a RequestError or UnknownValueError exception is caught,
|
41 |
-
# update the response object accordingly
|
42 |
-
try:
|
43 |
-
response["transcription"] = recognizer.recognize_google(audio)
|
44 |
-
except sr.RequestError:
|
45 |
-
# API was unreachable or unresponsive
|
46 |
-
response["success"] = False
|
47 |
-
response["error"] = "API unavailable"
|
48 |
-
except sr.UnknownValueError:
|
49 |
-
# speech was unintelligible
|
50 |
-
response["error"] = "Unable to recognize speech"
|
51 |
-
|
52 |
-
return response
|
53 |
-
|
54 |
-
|
55 |
-
if __name__ == "__main__":
|
56 |
-
# set the list of words, maxnumber of guesses, and prompt limit
|
57 |
-
WORDS = ["apple", "banana", "grape", "orange", "mango", "lemon"]
|
58 |
-
NUM_GUESSES = 3
|
59 |
-
PROMPT_LIMIT = 5
|
60 |
-
|
61 |
-
# create recognizer and mic instances
|
62 |
-
recognizer = sr.Recognizer()
|
63 |
-
microphone = sr.Microphone()
|
64 |
-
|
65 |
-
# get a random word from the list
|
66 |
-
word = random.choice(WORDS)
|
67 |
-
|
68 |
-
# format the instructions string
|
69 |
-
instructions = (
|
70 |
-
"I'm thinking of one of these words:\n"
|
71 |
-
"{words}\n"
|
72 |
-
"You have {n} tries to guess which one.\n"
|
73 |
-
).format(words=', '.join(WORDS), n=NUM_GUESSES)
|
74 |
-
|
75 |
-
# show instructions and wait 3 seconds before starting the game
|
76 |
-
print(instructions)
|
77 |
-
time.sleep(3)
|
78 |
-
|
79 |
-
for i in range(NUM_GUESSES):
|
80 |
-
# get the guess from the user
|
81 |
-
# if a transcription is returned, break out of the loop and
|
82 |
-
# continue
|
83 |
-
# if no transcription returned and API request failed, break
|
84 |
-
# loop and continue
|
85 |
-
# if API request succeeded but no transcription was returned,
|
86 |
-
# re-prompt the user to say their guess again. Do this up
|
87 |
-
# to PROMPT_LIMIT times
|
88 |
-
for j in range(PROMPT_LIMIT):
|
89 |
-
print('Guess {}. Speak!'.format(i+1))
|
90 |
-
guess = recognize_speech_from_mic(recognizer, microphone)
|
91 |
-
if guess["transcription"]:
|
92 |
-
break
|
93 |
-
if not guess["success"]:
|
94 |
-
break
|
95 |
-
print("I didn't catch that. What did you say?\n")
|
96 |
-
|
97 |
-
# if there was an error, stop the game
|
98 |
-
if guess["error"]:
|
99 |
-
print("ERROR: {}".format(guess["error"]))
|
100 |
-
break
|
101 |
-
|
102 |
-
# show the user the transcription
|
103 |
-
print("You said: {}".format(guess["transcription"]))
|
104 |
-
|
105 |
-
# determine if guess is correct and if any attempts remain
|
106 |
-
guess_is_correct = guess["transcription"].lower() == word.lower()
|
107 |
-
user_has_more_attempts = i < NUM_GUESSES - 1
|
108 |
-
|
109 |
-
# determine if the user has won the game
|
110 |
-
# if not, repeat the loop if user has more attempts
|
111 |
-
# if no attempts left, the user loses the game
|
112 |
-
if guess_is_correct:
|
113 |
-
print("Correct! You win!".format(word))
|
114 |
-
break
|
115 |
-
elif user_has_more_attempts:
|
116 |
-
print("Incorrect. Try again.\n")
|
117 |
-
else:
|
118 |
-
print("Sorry, you lose!\nI was thinking of '{}'.".format(word))
|
119 |
-
break
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/hello.py
DELETED
@@ -1,7 +0,0 @@
|
|
1 |
-
import llama_cpp
|
2 |
-
model = llama_cpp.Llama(
|
3 |
-
model_path="/home/mo/Desktop/web/oGBackend/qwen1_5-0_5b-chat-q2_k.gguf",
|
4 |
-
chat_format="llama-2",
|
5 |
-
)
|
6 |
-
x = model.create_completion("Write a story about robotics?")
|
7 |
-
print(x)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/models/__pycache__/user.cpython-310.pyc
DELETED
Binary file (562 Bytes)
|
|
app/models/user.py
DELETED
@@ -1,9 +0,0 @@
|
|
1 |
-
from sqlalchemy import Column, Integer, String
|
2 |
-
from app.db.database import Base
|
3 |
-
|
4 |
-
class User(Base):
|
5 |
-
__tablename__ = "users"
|
6 |
-
|
7 |
-
id = Column(Integer, primary_key=True, index=True)
|
8 |
-
username = Column(String, unique=True, index=True)
|
9 |
-
password = Column(String)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/routers/__pycache__/llm.cpython-310.pyc
DELETED
Binary file (3.62 kB)
|
|
app/routers/__pycache__/user.cpython-310.pyc
DELETED
Binary file (1.59 kB)
|
|
app/routers/llm.py
DELETED
@@ -1,136 +0,0 @@
|
|
1 |
-
from fastapi import APIRouter, Depends, HTTPException, status
|
2 |
-
from sqlalchemy.orm import Session
|
3 |
-
from app.db.database import get_db
|
4 |
-
from app.models.user import User
|
5 |
-
from app.schemas.user import UserCreate, UserOut
|
6 |
-
from app.auth import create_access_token, get_current_user
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
import fastapi
|
11 |
-
from fastapi.responses import JSONResponse
|
12 |
-
from time import time
|
13 |
-
#from fastapi.middleware.cors import CORSMiddleware
|
14 |
-
import logging
|
15 |
-
import llama_cpp
|
16 |
-
import llama_cpp.llama_tokenizer
|
17 |
-
from pydantic import BaseModel
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
router = APIRouter(prefix="/llm", tags=["llm"])
|
24 |
-
|
25 |
-
|
26 |
-
class GenModel(BaseModel):
|
27 |
-
question: str
|
28 |
-
system: str = "You are a helpful medical AI chat assistant. Help as much as you can.Also continuously ask for possible symptoms in order to atat a conclusive ailment or sickness and possible solutions.Remember, response in English."
|
29 |
-
temperature: float = 0.8
|
30 |
-
seed: int = 101
|
31 |
-
mirostat_mode: int=2
|
32 |
-
mirostat_tau: float=4.0
|
33 |
-
mirostat_eta: float=1.1
|
34 |
-
|
35 |
-
class ChatModel(BaseModel):
|
36 |
-
question: list
|
37 |
-
system: str = "You are a helpful medical AI chat assistant. Help as much as you can.Also continuously ask for possible symptoms in order to atat a conclusive ailment or sickness and possible solutions.Remember, response in English."
|
38 |
-
temperature: float = 0.8
|
39 |
-
seed: int = 101
|
40 |
-
mirostat_mode: int=2
|
41 |
-
mirostat_tau: float=4.0
|
42 |
-
mirostat_eta: float=1.1
|
43 |
-
llm_chat = llama_cpp.Llama.from_pretrained(
|
44 |
-
repo_id="Qwen/Qwen1.5-0.5B-Chat-GGUF",
|
45 |
-
filename="*q4_0.gguf",
|
46 |
-
tokenizer=llama_cpp.llama_tokenizer.LlamaHFTokenizer.from_pretrained("Qwen/Qwen1.5-0.5B"),
|
47 |
-
verbose=False,
|
48 |
-
n_ctx=1024,
|
49 |
-
n_gpu_layers=0,
|
50 |
-
#chat_format="llama-2"
|
51 |
-
)
|
52 |
-
llm_generate = llama_cpp.Llama.from_pretrained(
|
53 |
-
repo_id="Qwen/Qwen1.5-0.5B-Chat-GGUF",
|
54 |
-
filename="*q4_0.gguf",
|
55 |
-
tokenizer=llama_cpp.llama_tokenizer.LlamaHFTokenizer.from_pretrained("Qwen/Qwen1.5-0.5B"),
|
56 |
-
verbose=False,
|
57 |
-
n_ctx=4096,
|
58 |
-
n_gpu_layers=0,
|
59 |
-
mirostat_mode=2,
|
60 |
-
mirostat_tau=4.0,
|
61 |
-
mirostat_eta=1.1
|
62 |
-
#chat_format="llama-2"
|
63 |
-
)
|
64 |
-
# Logger setup
|
65 |
-
logging.basicConfig(level=logging.INFO)
|
66 |
-
logger = logging.getLogger(__name__)
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
@router.get("/")
|
72 |
-
def index():
|
73 |
-
return fastapi.responses.RedirectResponse(url="/docs")
|
74 |
-
|
75 |
-
@router.get("/health")
|
76 |
-
def health():
|
77 |
-
return {"status": "ok"}
|
78 |
-
|
79 |
-
# Chat Completion API
|
80 |
-
@router.post("/chat/")
|
81 |
-
async def chat(chatm:ChatModel):
|
82 |
-
try:
|
83 |
-
st = time()
|
84 |
-
output = llm_chat.create_chat_completion(
|
85 |
-
messages = chatm.question,
|
86 |
-
temperature = chatm.temperature,
|
87 |
-
seed = chatm.seed,
|
88 |
-
#stream=True
|
89 |
-
)
|
90 |
-
#print(output)
|
91 |
-
et = time()
|
92 |
-
output["time"] = et - st
|
93 |
-
#messages.append({'role': "assistant", "content": output['choices'][0]['message']['content']})
|
94 |
-
#print(messages)
|
95 |
-
return output
|
96 |
-
except Exception as e:
|
97 |
-
logger.error(f"Error in /complete endpoint: {e}")
|
98 |
-
return JSONResponse(
|
99 |
-
status_code=500, content={"message": "Internal Server Error"}
|
100 |
-
)
|
101 |
-
|
102 |
-
# Chat Completion API
|
103 |
-
@router.post("/generate")
|
104 |
-
async def generate(gen:GenModel):
|
105 |
-
gen.system = "You are an helpful medical AI assistant."
|
106 |
-
gen.temperature = 0.5
|
107 |
-
gen.seed = 42
|
108 |
-
try:
|
109 |
-
st = time()
|
110 |
-
output = llm_generate.create_chat_completion(
|
111 |
-
messages=[
|
112 |
-
{"role": "system", "content": gen.system},
|
113 |
-
{"role": "user", "content": gen.question},
|
114 |
-
],
|
115 |
-
temperature = gen.temperature,
|
116 |
-
seed= gen.seed,
|
117 |
-
#stream=True,
|
118 |
-
#echo=True
|
119 |
-
)
|
120 |
-
"""
|
121 |
-
for chunk in output:
|
122 |
-
delta = chunk['choices'][0]['delta']
|
123 |
-
if 'role' in delta:
|
124 |
-
print(delta['role'], end=': ')
|
125 |
-
elif 'content' in delta:
|
126 |
-
print(delta['content'], end='')
|
127 |
-
#print(chunk)
|
128 |
-
"""
|
129 |
-
et = time()
|
130 |
-
output["time"] = et - st
|
131 |
-
return output
|
132 |
-
except Exception as e:
|
133 |
-
logger.error(f"Error in /generate endpoint: {e}")
|
134 |
-
return JSONResponse(
|
135 |
-
status_code=500, content={"message": "Internal Server Error"}
|
136 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/routers/user.py
DELETED
@@ -1,38 +0,0 @@
|
|
1 |
-
from fastapi import APIRouter, Depends, HTTPException, status
|
2 |
-
from sqlalchemy.orm import Session
|
3 |
-
from app.db.database import get_db
|
4 |
-
from app.models.user import User
|
5 |
-
from app.schemas.user import UserCreate, UserOut
|
6 |
-
from app.auth import create_access_token, get_current_user
|
7 |
-
|
8 |
-
router = APIRouter(prefix="/user", tags=["user"])
|
9 |
-
|
10 |
-
@router.post("/register", response_model=UserOut)
|
11 |
-
def register(user: UserCreate, db: Session = Depends(get_db)):
|
12 |
-
existing_user = db.query(User).filter(User.username == user.username).first()
|
13 |
-
if existing_user:
|
14 |
-
raise HTTPException(status_code=400,
|
15 |
-
#status.HTTP_400_BAD_REQUEST,
|
16 |
-
detail="Username already taken")
|
17 |
-
|
18 |
-
hashed_password = user.password # Hashing is needed here
|
19 |
-
db_user = User(username=user.username, password=hashed_password)
|
20 |
-
db.add(db_user)
|
21 |
-
db.commit()
|
22 |
-
db.refresh(db_user)
|
23 |
-
return db_user
|
24 |
-
|
25 |
-
@router.post("/login")
|
26 |
-
def login(user: UserCreate, db: Session = Depends(get_db)):
|
27 |
-
db_user = db.query(User).filter(User.username == user.username).first()
|
28 |
-
if not db_user or db_user.password != user.password:
|
29 |
-
raise HTTPException(status_code=400,
|
30 |
-
#status.HTTP.400_BAD_REQUEST,
|
31 |
-
detail="Invalid credentials")
|
32 |
-
|
33 |
-
access_token = create_access_token(data={"sub": str(db_user.id)})
|
34 |
-
return {"access_token": access_token, "token_type": "bearer"}
|
35 |
-
|
36 |
-
@router.get("/me", response_model=UserOut)
|
37 |
-
def read_users_me(current_user: User = Depends(get_current_user)):
|
38 |
-
return current_user
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/schemas/__pycache__/user.cpython-310.pyc
DELETED
Binary file (947 Bytes)
|
|
app/schemas/user.py
DELETED
@@ -1,17 +0,0 @@
|
|
1 |
-
from pydantic import BaseModel
|
2 |
-
|
3 |
-
class UserInDB(BaseModel):
|
4 |
-
id: int
|
5 |
-
username: str
|
6 |
-
password: str
|
7 |
-
|
8 |
-
class UserCreate(BaseModel):
|
9 |
-
username: str
|
10 |
-
password: str
|
11 |
-
|
12 |
-
class UserOut(BaseModel):
|
13 |
-
id: int
|
14 |
-
username: str
|
15 |
-
|
16 |
-
class Config:
|
17 |
-
orm_mode = True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/security.py
DELETED
@@ -1,9 +0,0 @@
|
|
1 |
-
from passlib.context import CryptContext
|
2 |
-
|
3 |
-
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
4 |
-
|
5 |
-
def hash_password(password: str):
|
6 |
-
return pwd_context.hash(password)
|
7 |
-
|
8 |
-
def verify_password(plain_password: str, hashed_password: str):
|
9 |
-
return pwd_context.verify(plain_password, hashed_password)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
crud.py
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from sqlalchemy.orm import Session
|
2 |
+
|
3 |
+
import models
|
4 |
+
import schemas
|
5 |
+
|
6 |
+
|
7 |
+
def get_user(db: Session, user_id: int):
|
8 |
+
return db.query(models.User).filter(models.User.id == user_id).first()
|
9 |
+
|
10 |
+
|
11 |
+
def get_user_by_email(db: Session, email: str):
|
12 |
+
return db.query(models.User).filter(models.User.email == email).first()
|
13 |
+
|
14 |
+
|
15 |
+
def get_users(db: Session, skip: int = 0, limit: int = 100):
|
16 |
+
return db.query(models.User).offset(skip).limit(limit).all()
|
17 |
+
|
18 |
+
|
19 |
+
def create_user(db: Session, user: schemas.UserCreate):
|
20 |
+
fake_hashed_password = user.password + "notreallyhashed"
|
21 |
+
db_user = models.User(email=user.email, hashed_password=fake_hashed_password)
|
22 |
+
db.add(db_user)
|
23 |
+
db.commit()
|
24 |
+
db.refresh(db_user)
|
25 |
+
return db_user
|
26 |
+
|
27 |
+
|
28 |
+
def get_items(db: Session, skip: int = 0, limit: int = 100):
|
29 |
+
return db.query(models.Item).offset(skip).limit(limit).all()
|
30 |
+
|
31 |
+
|
32 |
+
def create_user_item(db: Session, item: schemas.ItemCreate, user_id: int):
|
33 |
+
db_item = models.Item(**item.model_dump(), owner_id=user_id)
|
34 |
+
db.add(db_item)
|
35 |
+
db.commit()
|
36 |
+
db.refresh(db_item)
|
37 |
+
return db_item
|
database.py
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from sqlalchemy import create_engine
|
2 |
+
from sqlalchemy.ext.declarative import declarative_base
|
3 |
+
from sqlalchemy.orm import sessionmaker
|
4 |
+
|
5 |
+
SQLALCHEMY_DATABASE_URL = "sqlite:///./sql_app.db"
|
6 |
+
# SQLALCHEMY_DATABASE_URL = "postgresql://user:password@postgresserver/db"
|
7 |
+
|
8 |
+
engine = create_engine(
|
9 |
+
SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
|
10 |
+
)
|
11 |
+
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
12 |
+
|
13 |
+
Base = declarative_base()
|
main.py
CHANGED
@@ -1,29 +1,66 @@
|
|
1 |
-
|
2 |
-
from
|
3 |
-
|
4 |
-
|
|
|
|
|
|
|
|
|
|
|
5 |
|
6 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
|
8 |
-
|
9 |
-
app.include_router(user.router)
|
10 |
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
|
18 |
-
#Base.metadata.create_all(bind=engine)
|
19 |
|
20 |
-
|
21 |
-
|
|
|
|
|
22 |
|
23 |
|
24 |
-
@
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import Depends, FastAPI, HTTPException
|
2 |
+
from sqlalchemy.orm import Session
|
3 |
+
import crud
|
4 |
+
import models
|
5 |
+
import schemas
|
6 |
+
from database import SessionLocal, engine
|
7 |
+
from app import llm_router
|
8 |
+
from fastapi import APIRouter, FastAPI
|
9 |
+
models.Base.metadata.create_all(bind=engine)
|
10 |
|
11 |
+
# Dependency
|
12 |
+
def get_db():
|
13 |
+
db = SessionLocal()
|
14 |
+
try:
|
15 |
+
yield db
|
16 |
+
finally:
|
17 |
+
db.close()
|
18 |
|
19 |
+
user_router = APIRouter()
|
|
|
20 |
|
21 |
+
@user_router.post("/users/", response_model=schemas.User)
|
22 |
+
def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)):
|
23 |
+
db_user = crud.get_user_by_email(db, email=user.email)
|
24 |
+
if db_user:
|
25 |
+
raise HTTPException(status_code=400, detail="Email already registered")
|
26 |
+
return crud.create_user(db=db, user=user)
|
27 |
|
|
|
28 |
|
29 |
+
@user_router.get("/users/", response_model=list[schemas.User])
|
30 |
+
def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
|
31 |
+
users = crud.get_users(db, skip=skip, limit=limit)
|
32 |
+
return users
|
33 |
|
34 |
|
35 |
+
@user_router.get("/users/{user_id}", response_model=schemas.User)
|
36 |
+
def read_user(user_id: int, db: Session = Depends(get_db)):
|
37 |
+
db_user = crud.get_user(db, user_id=user_id)
|
38 |
+
if db_user is None:
|
39 |
+
raise HTTPException(status_code=404, detail="User not found")
|
40 |
+
return db_user
|
41 |
+
|
42 |
+
|
43 |
+
@user_router.post("/users/{user_id}/items/", response_model=schemas.Item)
|
44 |
+
def create_item_for_user(
|
45 |
+
user_id: int, item: schemas.ItemCreate, db: Session = Depends(get_db)
|
46 |
+
):
|
47 |
+
return crud.create_user_item(db=db, item=item, user_id=user_id)
|
48 |
+
|
49 |
+
|
50 |
+
@user_router.get("/items/", response_model=list[schemas.Item])
|
51 |
+
def read_items(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
|
52 |
+
items = crud.get_items(db, skip=skip, limit=limit)
|
53 |
+
return items
|
54 |
+
|
55 |
+
|
56 |
+
@user_router.get("/")
|
57 |
+
async def root():
|
58 |
+
return {"message": "Hello World"}
|
59 |
+
|
60 |
+
|
61 |
+
app = FastAPI(
|
62 |
+
title="OpenGenAI",
|
63 |
+
description="Your Excellect AI Physician")
|
64 |
+
|
65 |
+
app.include_router(user_router)
|
66 |
+
app.include_router(llm_router)
|
models.py
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String
|
2 |
+
from sqlalchemy.orm import relationship
|
3 |
+
|
4 |
+
from database import Base
|
5 |
+
|
6 |
+
|
7 |
+
class User(Base):
|
8 |
+
__tablename__ = "users"
|
9 |
+
|
10 |
+
id = Column(Integer, primary_key=True, index=True)
|
11 |
+
email = Column(String, unique=True, index=True)
|
12 |
+
hashed_password = Column(String)
|
13 |
+
is_active = Column(Boolean, default=True)
|
14 |
+
|
15 |
+
items = relationship("Item", back_populates="owner")
|
16 |
+
|
17 |
+
|
18 |
+
class Item(Base):
|
19 |
+
__tablename__ = "items"
|
20 |
+
|
21 |
+
id = Column(Integer, primary_key=True, index=True)
|
22 |
+
title = Column(String, index=True)
|
23 |
+
description = Column(String, index=True)
|
24 |
+
owner_id = Column(Integer, ForeignKey("users.id"))
|
25 |
+
|
26 |
+
owner = relationship("User", back_populates="items")
|
requirements.txt
CHANGED
Binary files a/requirements.txt and b/requirements.txt differ
|
|
schemas.py
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from typing import Union
|
2 |
+
from pydantic import BaseModel
|
3 |
+
|
4 |
+
|
5 |
+
class ItemBase(BaseModel):
|
6 |
+
title: str
|
7 |
+
description: Union[str, None] = None
|
8 |
+
|
9 |
+
|
10 |
+
class ItemCreate(ItemBase):
|
11 |
+
pass
|
12 |
+
|
13 |
+
|
14 |
+
class Item(ItemBase):
|
15 |
+
id: int
|
16 |
+
owner_id: int
|
17 |
+
|
18 |
+
class Config:
|
19 |
+
from_attributes = True
|
20 |
+
|
21 |
+
|
22 |
+
class UserBase(BaseModel):
|
23 |
+
email: str
|
24 |
+
|
25 |
+
|
26 |
+
class UserCreate(UserBase):
|
27 |
+
password: str
|
28 |
+
|
29 |
+
|
30 |
+
class User(UserBase):
|
31 |
+
id: int
|
32 |
+
is_active: bool
|
33 |
+
items: list[Item] = []
|
34 |
+
|
35 |
+
class Config:
|
36 |
+
from_attributes = True
|
sql_app.db
ADDED
Binary file (32.8 kB). View file
|
|