commit 7dd5a51872e653356136849dc83b16d6cd4f8385 Author: kuzakhmetovartur Date: Sun Jun 28 12:42:48 2026 +0300 feat: solution for 'сырой текст задания → плоская карточка' diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b16538b --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +node_modules/ +.env +dist/ +build/ +*.log diff --git a/3 b/3 new file mode 160000 index 0000000..ddd2bdb --- /dev/null +++ b/3 @@ -0,0 +1 @@ +Subproject commit ddd2bdb8a91a090d31fd71eb089d83fc900444ef diff --git a/8-deep-agents-from-scratch b/8-deep-agents-from-scratch new file mode 160000 index 0000000..380e236 --- /dev/null +++ b/8-deep-agents-from-scratch @@ -0,0 +1 @@ +Subproject commit 380e236ecf06e77b69962b9bc9a40925b6063211 diff --git a/README.md b/README.md new file mode 100644 index 0000000..65d6849 --- /dev/null +++ b/README.md @@ -0,0 +1,84 @@ +# Assignment Card Extraction + +This project demonstrates how to convert a natural language assignment description into a structured data object using **LangChain** and **Pydantic**. + +## Features + +- Parses assignment details such as title, subject, deadline, deliverable type, and grading hints. +- Uses OpenAI's GPT model to interpret free‑form text. +- Validates the output with a Pydantic model to ensure type safety. + +## Prerequisites + +- Python 3.10 or newer +- An OpenAI API key + +## Setup + +1. **Clone the repository** (or copy the files into a directory): + + ```bash + git clone https://github.com/your-username/assignment-card-extractor.git + cd assignment-card-extractor + ``` + +2. **Create a virtual environment** (recommended): + + ```bash + python -m venv .venv + source .venv/bin/activate # On Windows: .venv\Scripts\activate + ``` + +3. **Install dependencies**: + + ```bash + pip install -r requirements.txt + ``` + +4. **Configure the OpenAI API key**: + + - Open the `.env` file. + - Replace `your_openai_api_key_here` with your actual key. + + ```dotenv + OPENAI_API_KEY=sk-XXXXXXXXXXXXXXXXXXXX + ``` + +## Running the Example + +```bash +python src/main.py +``` + +You should see output similar to: + +``` +=== Parsed Assignment Card === +{ + "title": "Мини-отчёт по LangChain", + "subject": "LangChain", + "deadline_hint": "к пятнице", + "deliverable_type": "отчёт", + "grading_hints": [ + "полнота", + "пример кода" + ] +} + +=== Human-readable Summary === +Title: Мини-отчёт по LangChain +Subject: LangChain +Deadline: к пятнице +Deliverable: отчёт +Grading Hints: полнота, пример кода +``` + +## Customization + +- **Change the LLM**: Edit `src/main.py` to use a different model or adjust temperature. +- **Add more fields**: Update the `AssignmentCard` model and the prompt template accordingly. +- **Use in a pipeline**: Import the `chain` object from `src/main.py` into your own application. + +## License + +MIT License \ No newline at end of file diff --git a/human-in-the-loop-interrupt-resume b/human-in-the-loop-interrupt-resume new file mode 160000 index 0000000..3c81f16 --- /dev/null +++ b/human-in-the-loop-interrupt-resume @@ -0,0 +1 @@ +Subproject commit 3c81f16ab4d8f130f8101fba7552fb239b275075 diff --git a/human-in-the-loop-middleware b/human-in-the-loop-middleware new file mode 160000 index 0000000..082d5fb --- /dev/null +++ b/human-in-the-loop-middleware @@ -0,0 +1 @@ +Subproject commit 082d5fb669012b2aace90b1d2e17b9eb394e386c diff --git a/llm-interrupt b/llm-interrupt new file mode 160000 index 0000000..67ab81d --- /dev/null +++ b/llm-interrupt @@ -0,0 +1 @@ +Subproject commit 67ab81df8fd742eb72d2dba19b3d04f0514a06a8 diff --git a/mcp b/mcp new file mode 160000 index 0000000..1fbb6de --- /dev/null +++ b/mcp @@ -0,0 +1 @@ +Subproject commit 1fbb6def58e23715afcbd28c441aee06fa56e3ed diff --git a/rag b/rag new file mode 160000 index 0000000..a8a8111 --- /dev/null +++ b/rag @@ -0,0 +1 @@ +Subproject commit a8a8111eca213d3b90d529ac5c145e2cce48726e diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..4711f44 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +langchain-core>=0.2.0 +langchain-openai>=0.2.0 +pydantic>=2.0 +python-dotenv>=1.0 \ No newline at end of file diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..4be24f1 --- /dev/null +++ b/src/main.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Assignment Card Extraction + +This script demonstrates how to extract structured assignment details from a +natural language description using LangChain and Pydantic. +""" + +import os +from typing import List + +from dotenv import load_dotenv +from langchain_core.output_parsers import PydanticOutputParser +from langchain_core.prompts import PromptTemplate +from langchain_openai import ChatOpenAI +from pydantic import BaseModel, Field + +# Load environment variables (expects OPENAI_API_KEY) +load_dotenv() + +# --------------------------------------------------------------------------- # +# Pydantic model definition +# --------------------------------------------------------------------------- # +class AssignmentCard(BaseModel): + """ + Structured representation of an assignment description. + """ + + title: str = Field( + ..., + description="Short title of the assignment (e.g., 'Mini-report on LangChain').", + ) + subject: str = Field( + ..., + description="Subject or topic of the assignment (e.g., 'LangChain').", + ) + deadline_hint: str = Field( + ..., + description="A short phrase indicating the deadline (e.g., 'by Friday').", + ) + deliverable_type: str = Field( + ..., + description="What to submit: report, code, presentation, etc.", + ) + grading_hints: List[str] = Field( + ..., + description="List of key grading criteria mentioned in the description.", + ) + +# --------------------------------------------------------------------------- # +# LangChain components +# --------------------------------------------------------------------------- # +# Parser that will convert the LLM output into an AssignmentCard instance +parser = PydanticOutputParser(pydantic_object=AssignmentCard) + +# Prompt template that instructs the LLM to output JSON matching the model +prompt = PromptTemplate( + template=( + "You are an assignment extraction assistant. " + "Given the following assignment description, extract the following fields:\n\n" + "- title: short title of the assignment\n" + "- subject: subject or topic\n" + "- deadline_hint: a short phrase indicating the deadline\n" + "- deliverable_type: what to submit (e.g., report, code, presentation)\n" + "- grading_hints: list of key grading criteria mentioned\n\n" + "Return a JSON object with exactly these keys. Do not include any additional keys or text.\n\n" + "Description: {description}\n\n" + "{format_instructions}" + ), + input_variables=["description"], + partial_variables={"format_instructions": parser.get_format_instructions()}, +) + +# LLM configuration +llm = ChatOpenAI( + temperature=0, + model="gpt-3.5-turbo", +) + +# Chain: prompt -> LLM -> parser +chain = prompt | llm | parser + +# --------------------------------------------------------------------------- # +# Main execution +# --------------------------------------------------------------------------- # +def main() -> None: + # Sample assignment description + sample_description = ( + "Сдайте к пятнице мини-отчёт по LangChain: 2 страницы, упор на агентов. " + "Оценка: за полноту и за пример кода." + ) + + # Run the chain + try: + result = chain.invoke({"description": sample_description}) + except Exception as e: + print(f"Error during chain execution: {e}") + return + + # The result is already a validated AssignmentCard instance + print("\n=== Parsed Assignment Card ===") + print(result.model_dump(indent=2)) + + # Human-readable summary + print("\n=== Human-readable Summary ===") + print(f"Title: {result.title}") + print(f"Subject: {result.subject}") + print(f"Deadline: {result.deadline_hint}") + print(f"Deliverable: {result.deliverable_type}") + print(f"Grading Hints: {', '.join(result.grading_hints)}") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/untitled-task/README.md b/untitled-task/README.md new file mode 100644 index 0000000..0aebf39 --- /dev/null +++ b/untitled-task/README.md @@ -0,0 +1,66 @@ +# Login App + +A simple React application demonstrating a login form with email and password fields, along with "Forgot password?" and "Register" links that navigate to their respective routes. + +## Features + +- **Login Form**: Email and password inputs with basic validation. +- **Routing**: Uses `react-router-dom` for navigation between login, forgot password, and register pages. +- **Minimal Styling**: Basic CSS to make the UI clean and functional. + +## Getting Started + +### Prerequisites + +- Node.js (v14 or newer) +- npm (v6 or newer) + +### Installation + +```bash +# Clone the repository +git clone https://github.com/your-username/login-app.git +cd login-app + +# Install dependencies +npm install +``` + +### Running the App + +```bash +npm start +``` + +Open your browser and navigate to `http://localhost:3000`. You should see the login page. + +### Building for Production + +```bash +npm run build +``` + +The production-ready files will be in the `build/` directory. + +## Project Structure + +``` +login-app/ +├── node_modules/ +├── public/ +├── src/ +│ ├── components/ +│ │ ├── ForgotPassword.js +│ │ ├── Login.js +│ │ ├── Login.css +│ │ └── Register.js +│ ├── App.js +│ ├── index.css +│ └── index.js +├── package.json +└── README.md +``` + +## License + +This project is open source and available under the MIT License. \ No newline at end of file diff --git a/untitled-task/package.json b/untitled-task/package.json new file mode 100644 index 0000000..1cb0546 --- /dev/null +++ b/untitled-task/package.json @@ -0,0 +1,17 @@ +{ + "name": "login-app", + "version": "0.1.0", + "private": true, + "dependencies": { + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.14.1", + "react-scripts": "5.0.1" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject" + } +} \ No newline at end of file diff --git a/untitled-task/src/App.js b/untitled-task/src/App.js new file mode 100644 index 0000000..d7fbd4d --- /dev/null +++ b/untitled-task/src/App.js @@ -0,0 +1,19 @@ +import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom'; +import Login from './components/Login'; +import ForgotPassword from './components/ForgotPassword'; +import Register from './components/Register'; + +function App() { + return ( + + + } /> + } /> + } /> + } /> + + + ); +} + +export default App; \ No newline at end of file diff --git a/untitled-task/src/App.tsx b/untitled-task/src/App.tsx new file mode 100644 index 0000000..e9cd6b7 --- /dev/null +++ b/untitled-task/src/App.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import { Routes, Route, Navigate } from 'react-router-dom'; +import LoginForm from './components/LoginForm'; +import { Box, Typography } from '@mui/material'; + +const RegisterPage: React.FC = () => ( + + Register Page + Registration form will go here. + +); + +const ForgotPasswordPage: React.FC = () => ( + + Forgot Password + Forgot password form will go here. + +); + +const HomePage: React.FC = () => ( + + Welcome to the App + Use the navigation to login, register, or reset password. + +); + +const App: React.FC = () => { + return ( + + } /> + } /> + } /> + } /> + } /> + + ); +}; + +export default App; \ No newline at end of file diff --git a/untitled-task/src/components/ForgotPassword.js b/untitled-task/src/components/ForgotPassword.js new file mode 100644 index 0000000..1cab029 --- /dev/null +++ b/untitled-task/src/components/ForgotPassword.js @@ -0,0 +1,13 @@ +import { Link } from 'react-router-dom'; + +function ForgotPassword() { + return ( +
+

Forgot Password

+

This is a placeholder page for password recovery.

+ Back to Login +
+ ); +} + +export default ForgotPassword; \ No newline at end of file diff --git a/untitled-task/src/components/Login.css b/untitled-task/src/components/Login.css new file mode 100644 index 0000000..2685240 --- /dev/null +++ b/untitled-task/src/components/Login.css @@ -0,0 +1,38 @@ +.login-container { + max-width: 400px; + margin: 80px auto; + padding: 20px; + border: 1px solid #ddd; + border-radius: 8px; + background-color: #fafafa; + text-align: center; +} + +.login-form { + display: flex; + flex-direction: column; + gap: 15px; +} + +.login-form label { + display: flex; + flex-direction: column; + font-weight: 500; + text-align: left; +} + +.login-form input { + padding: 8px; + font-size: 1rem; + margin-top: 5px; +} + +.login-form button { + padding: 10px; + font-size: 1rem; + cursor: pointer; +} + +.login-links { + margin-top: 15px; +} \ No newline at end of file diff --git a/untitled-task/src/components/Login.js b/untitled-task/src/components/Login.js new file mode 100644 index 0000000..b299fa6 --- /dev/null +++ b/untitled-task/src/components/Login.js @@ -0,0 +1,55 @@ +import { useState } from 'react'; +import { Link, useNavigate } from 'react-router-dom'; +import './Login.css'; + +function Login() { + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const navigate = useNavigate(); + + const handleSubmit = (e) => { + e.preventDefault(); + // Placeholder for authentication logic + console.log('Email:', email); + console.log('Password:', password); + // After successful login, navigate to a protected route or dashboard + // navigate('/dashboard'); + }; + + return ( +
+

Login

+
+ + + + + +
+ +
+ Forgot password? + | + Register +
+
+ ); +} + +export default Login; \ No newline at end of file diff --git a/untitled-task/src/components/LoginForm.tsx b/untitled-task/src/components/LoginForm.tsx new file mode 100644 index 0000000..2f2910f --- /dev/null +++ b/untitled-task/src/components/LoginForm.tsx @@ -0,0 +1,121 @@ +import React, { useState, FormEvent } from 'react'; +import { + Box, + Button, + TextField, + Link, + Typography, + Stack, + Divider, +} from '@mui/material'; +import { Link as RouterLink } from 'react-router-dom'; +import GoogleIcon from '@mui/icons-material/Google'; +import FacebookIcon from '@mui/icons-material/Facebook'; + +const LoginForm: React.FC = () => { + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [errors, setErrors] = useState<{ email?: string; password?: string }>({}); + + const validate = () => { + const newErrors: { email?: string; password?: string } = {}; + if (!email) { + newErrors.email = 'Email is required'; + } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) { + newErrors.email = 'Invalid email address'; + } + if (!password) { + newErrors.password = 'Password is required'; + } else if (password.length < 6) { + newErrors.password = 'Password must be at least 6 characters'; + } + setErrors(newErrors); + return Object.keys(newErrors).length === 0; + }; + + const handleSubmit = (e: FormEvent) => { + e.preventDefault(); + if (!validate()) return; + console.log('Submitting', { email, password }); + // Placeholder for actual authentication logic + }; + + const handleThirdPartyLogin = (provider: string) => { + console.log(`Logging in with ${provider}`); + // Placeholder for third‑party auth + }; + + return ( + + + Sign In + + + setEmail(e.target.value)} + error={!!errors.email} + helperText={errors.email} + /> + setPassword(e.target.value)} + error={!!errors.password} + helperText={errors.password} + /> + + + Forgot password? + + + Register + + + + + + or + + + + + + + ); +}; + +export default LoginForm; \ No newline at end of file diff --git a/untitled-task/src/components/Register.js b/untitled-task/src/components/Register.js new file mode 100644 index 0000000..5dbb3e7 --- /dev/null +++ b/untitled-task/src/components/Register.js @@ -0,0 +1,13 @@ +import { Link } from 'react-router-dom'; + +function Register() { + return ( +
+

Register

+

This is a placeholder page for user registration.

+ Back to Login +
+ ); +} + +export default Register; \ No newline at end of file diff --git a/untitled-task/src/index.css b/untitled-task/src/index.css new file mode 100644 index 0000000..a2ef204 --- /dev/null +++ b/untitled-task/src/index.css @@ -0,0 +1,5 @@ +body { + margin: 0; + font-family: Arial, Helvetica, sans-serif; + background-color: #f0f2f5; +} \ No newline at end of file diff --git a/untitled-task/src/index.js b/untitled-task/src/index.js new file mode 100644 index 0000000..1e6f816 --- /dev/null +++ b/untitled-task/src/index.js @@ -0,0 +1,11 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import App from './App'; +import './index.css'; + +const root = ReactDOM.createRoot(document.getElementById('root')); +root.render( + + + +); \ No newline at end of file diff --git a/untitled-task/src/index.tsx b/untitled-task/src/index.tsx new file mode 100644 index 0000000..4ee957c --- /dev/null +++ b/untitled-task/src/index.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import { BrowserRouter } from 'react-router-dom'; +import App from './App'; +import CssBaseline from '@mui/material/CssBaseline'; + +const root = ReactDOM.createRoot( + document.getElementById('root') as HTMLElement +); +root.render( + + + + + + +); \ No newline at end of file