-
Notifications
You must be signed in to change notification settings - Fork 99
Задача 2 #167
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Задача 2 #167
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| import requests | ||
|
|
||
|
|
||
| class CloudFlareClient: | ||
| def __init__(self, api_token, account_id): | ||
| self.api_url = f"https://api.cloudflare.com/client/v4/accounts/{account_id}/ai/run/" | ||
| self.headers = { | ||
| "Authorization": f"Bearer {api_token}", | ||
| "Content-Type": "application/json" | ||
| } | ||
| self.inputs = [ | ||
| {"role": "system", "content": | ||
| "Отвечай по-русски, кратко (1–2 предложения)," | ||
| "как лучше выполнить задачу из списка дел. Без приветствий и воды." | ||
| "Отвечай естественно, без вводных фраз вроде «Для выполнения задачи…», «Чтобы сделать…», «Следует…»." | ||
| "Сразу переходи к сути," | ||
| "Если в задаче есть действие, пиши как" | ||
| " будто даёшь понятную инструкцию или совет, а не академическое объяснение"} | ||
| ] | ||
|
|
||
| def generate_answer(self,task): | ||
| data = {"messages": self.inputs + [{"role": "user", "content": task}]} | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. в идеале такое в pydantic модель перегонять сразу после получения http ответа |
||
| response = requests.post(f"{self.api_url}@cf/meta/llama-3-8b-instruct", headers=self.headers, json=data) | ||
|
|
||
|
|
||
| if response.status_code != 200: | ||
|
||
| print("Ошибка запроса:", response.status_code, response.text) | ||
| return None | ||
|
|
||
| result = response.json() | ||
| return result.get("result", {}).get("response", "⚠️ Нет ответа в JSON") | ||
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,19 +1,43 @@ | ||
| from fastapi import FastAPI | ||
| from fastapi import FastAPI, Body | ||
| from storage import JSONStorage, CloudJSONStorage | ||
| from models import Task, TaskCreate | ||
| import os | ||
| from dotenv import load_dotenv | ||
| from clients import CloudFlareClient | ||
|
|
||
| app = FastAPI() | ||
|
|
||
| load_dotenv() | ||
| BIN_ID = os.getenv('BIN_ID') | ||
| MASTER_KEY = os.getenv('MASTER_KEY') | ||
| storage = CloudJSONStorage(bin_id=BIN_ID, master_key=MASTER_KEY) | ||
|
|
||
| API_TOKEN_AI = os.getenv('API_TOKEN_AI') | ||
|
||
| ACCOUNT_ID_AI = os.getenv('ACCOUNT_ID_AI') | ||
| client_ai = CloudFlareClient(API_TOKEN_AI, ACCOUNT_ID_AI) | ||
|
|
||
|
|
||
| @app.get("/tasks") | ||
| def get_tasks(): | ||
| pass | ||
| return storage.get_task() | ||
|
|
||
| @app.post("/tasks") | ||
| def create_task(task): | ||
| pass | ||
| def create_task(task_data: TaskCreate): | ||
| ai_reply = client_ai.generate_answer(task_data.title) | ||
| task_data.title = f"{task_data.title} — {ai_reply}" | ||
| task = storage.create_task(task_data.model_dump()) | ||
| return task | ||
|
|
||
| @app.put("/tasks/{task_id}") | ||
| def update_task(task_id: int): | ||
| pass | ||
| def update_task(task_id: int, task_data: TaskCreate = Body(...)): | ||
|
||
| ai_reply = client_ai.generate_answer(task_data.title) | ||
| task_data.title = f"{task_data.title} — {ai_reply}" | ||
| is_update = storage.update_task(task_id, task_data.model_dump()) | ||
| if is_update: return is_update | ||
| return None | ||
|
|
||
| @app.delete("/tasks/{task_id}") | ||
| def delete_task(task_id: int): | ||
| pass | ||
| is_delete = storage.delete_task(task_id) | ||
| if is_delete: return True | ||
|
||
| return None | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| from pydantic import BaseModel | ||
|
|
||
| class TaskCreate(BaseModel): | ||
| title: str | ||
|
||
| status: bool | ||
|
|
||
| class Task(TaskCreate): | ||
| id: int | ||
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,105 @@ | ||
| import json | ||
| from pathlib import Path | ||
| import requests | ||
|
|
||
| class IsMemoryStorage: | ||
|
|
||
| def __init__(self): | ||
| self.tasks = [] | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. вообще можно было бы ещё общий интерфейс Storage сделать кстати |
||
| self.next_task = 1 | ||
|
|
||
|
|
||
| def list_tasks(self): | ||
|
||
| return self.tasks | ||
|
|
||
| def create_task(self, task_data): | ||
| task = {'id': self.next_task, **task_data} | ||
|
||
| self.tasks.append(task) | ||
| self.next_task += 1 | ||
|
|
||
| return task | ||
|
|
||
| def get_task(self, task_id): | ||
| for task in self.tasks: | ||
| if task['id'] == task_id: | ||
| return task | ||
| return None | ||
|
|
||
| def update_task(self, task_id:int, task_data): | ||
| for task in self.tasks: | ||
| if task['id'] == task_id: | ||
| task.update(task_data) | ||
| return task | ||
| return None | ||
|
|
||
|
|
||
| def delete_task(self, task_id): | ||
| task = self.get_task(task_id) | ||
| if not task: return False | ||
| self.tasks.remove(task) | ||
| return True | ||
|
|
||
|
|
||
| class JSONStorage: | ||
| def __init__(self, filepath: str | None = "tasks.json"): | ||
| self.file = Path(filepath) | ||
| if not self.file.exists(): | ||
| self.file.write_text("[]", encoding="utf-8") | ||
|
|
||
| def _load(self): | ||
| with self.file.open("r", encoding="utf-8") as f: | ||
| return json.load(f) | ||
|
|
||
| def _save(self, data): | ||
| with self.file.open("w", encoding="utf-8") as f: | ||
| json.dump(data, f, indent=4, ensure_ascii=False) | ||
|
|
||
| def get_task(self): | ||
| return self._load() | ||
|
|
||
| def create_task(self, task_data: dict): | ||
| tasks = self._load() | ||
| new_id = max([t["id"] for t in tasks], default=0) + 1 | ||
| task = {"id": new_id, **task_data} | ||
| tasks.append(task) | ||
| self._save(tasks) | ||
| return task | ||
|
|
||
| def update_task(self, task_id: int, new_data: dict): | ||
| tasks = self._load() | ||
| for task in tasks: | ||
| if task["id"] == task_id: | ||
| task.update(new_data) | ||
| self._save(tasks) | ||
| return task | ||
| return None | ||
|
|
||
| def delete_task(self, task_id: int): | ||
| tasks = self._load() | ||
| for task in tasks: | ||
| if task["id"] == task_id: | ||
| tasks.remove(task) | ||
| self._save(tasks) | ||
| return task | ||
| return None | ||
|
|
||
| class CloudJSONStorage(JSONStorage): | ||
| def __init__(self, bin_id: str, master_key: str): | ||
| super().__init__(filepath="tasks.json") | ||
| self.base_url = f"https://api.jsonbin.io/v3/b/{bin_id}" | ||
| self.headers = { | ||
| "X-Master-Key": master_key, | ||
| "Content-Type": "application/json", | ||
| } | ||
|
|
||
| def _load(self): | ||
| res = requests.get(f"{self.base_url}/latest", headers=self.headers) | ||
| res.raise_for_status() | ||
| record = res.json()["record"] | ||
| return record.get("tasks", []) | ||
|
|
||
| def _save(self, tasks): | ||
| payload = {"tasks": tasks} | ||
| res = requests.put(self.base_url, headers=self.headers, json=payload) | ||
| res.raise_for_status() | ||
| return res.json() | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
это можно просто не в init написать, а в классе над методом, потому что это все равно одинаково для всех объектов