fix: prisma + posts service

This commit is contained in:
KofK 2024-08-20 20:11:21 +10:00
parent 11082d93a5
commit 435b7f3fca
9 changed files with 2437 additions and 69 deletions

View File

@ -1,7 +1,7 @@
version: '3.8' version: '3.8'
services: services:
postgres: postgres:
image: postgres:latest image: pgvector/pgvector:pg16
container_name: postgres container_name: postgres
environment: environment:
POSTGRES_USER: user POSTGRES_USER: user

1226
back/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -20,12 +20,15 @@
"test:e2e": "jest --config ./test/jest-e2e.json" "test:e2e": "jest --config ./test/jest-e2e.json"
}, },
"dependencies": { "dependencies": {
"@langchain/community": "^0.2.28",
"@langchain/ollama": "^0.0.4",
"@nestjs/common": "^10.4.1", "@nestjs/common": "^10.4.1",
"@nestjs/core": "^10.4.1", "@nestjs/core": "^10.4.1",
"@nestjs/passport": "^10.0.3", "@nestjs/passport": "^10.0.3",
"@nestjs/platform-express": "^10.4.1", "@nestjs/platform-express": "^10.4.1",
"@prisma/client": "^5.18.0", "@prisma/client": "^5.18.0",
"dotenv": "^16.4.5", "dotenv": "^16.4.5",
"langchain": "^0.2.16",
"multer": "^1.4.5-lts.1", "multer": "^1.4.5-lts.1",
"passport": "^0.7.0", "passport": "^0.7.0",
"passport-http": "^0.3.0", "passport-http": "^0.3.0",

View File

@ -1,11 +1,13 @@
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client { generator client {
provider = "prisma-client-js" provider = "prisma-client-js"
binaryTargets = ["native", "debian-openssl-3.0.x"] binaryTargets = ["native", "debian-openssl-3.0.x"]
previewFeatures = ["postgresqlExtensions"]
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
extensions = [pgvector(map: "vector")]
} }
model Post { model Post {
@ -14,5 +16,5 @@ model Post {
media String? // URL или путь к файлу media String? // URL или путь к файлу
mediaType String? // "image", "video", "drawing", "code" mediaType String? // "image", "video", "drawing", "code"
code String? // Код, который нужно запустить code String? // Код, который нужно запустить
//TODO Добавить vector для оценки близости постов embedding Float[] //Unsupported("vector")?
} }

View File

@ -8,7 +8,7 @@ export class PostsController {
constructor(private readonly postsService: PostsService) {} constructor(private readonly postsService: PostsService) {}
@Post() @Post()
async create(@Body() createPostDto: { text?: string; media?: string; mediaType?: string; code?: string }) { async create(@Body() createPostDto: { text?: string; media?: string; mediaType?: string; code?: string; embedding?: number[] }) {
return this.postsService.create(createPostDto); return this.postsService.create(createPostDto);
} }

View File

@ -1,11 +1,30 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { PrismaService } from '../prisma.service'; import { PrismaService } from '../prisma.service';
import { MemoryVectorStore } from 'langchain/vectorstores/memory';
import { RecursiveCharacterTextSplitter } from 'langchain/text_splitter';
import { OllamaEmbeddings } from '@langchain/ollama';
const embeddings = new OllamaEmbeddings({
model: 'llama2',
baseUrl: 'http://host.docker.internal:11434',
});
@Injectable() @Injectable()
export class PostsService { export class PostsService {
constructor(private readonly prisma: PrismaService) {} constructor(private readonly prisma: PrismaService) {}
async create(data: { text?: string; media?: string; mediaType?: string; code?: string }) { async create(data: { text?: string; media?: string; mediaType?: string; code?: string; embedding?: number[] }) {
const splitter = RecursiveCharacterTextSplitter.fromLanguage("markdown", {
chunkSize: 500,
chunkOverlap: 0,
});
const output = await splitter.createDocuments([data.text]);
console.log(output)
const vectorstore = await MemoryVectorStore.fromDocuments(output, embeddings);
console.log(vectorstore);
data.embedding ??= vectorstore.memoryVectors[0].embedding
return this.prisma.post.create({ return this.prisma.post.create({
data, data,
}); });

1169
front/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -7,8 +7,11 @@
"@testing-library/react": "^13.4.0", "@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0", "@testing-library/user-event": "^13.5.0",
"axios": "^1.7.4", "axios": "^1.7.4",
"markdown-it": "^14.1.0",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-markdown": "^9.0.1",
"react-markdown-editor-lite": "^1.3.4",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"web-vitals": "^2.1.4" "web-vitals": "^2.1.4"
}, },

View File

@ -1,12 +1,16 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import axios from 'axios'; import axios from 'axios';
import ReactMarkdown from 'react-markdown';
import MarkdownEditor from 'react-markdown-editor-lite';
import 'react-markdown-editor-lite/lib/index.css';
import MarkdownIt from 'markdown-it';
// Инициализация MarkdownIt
const mdParser = new MarkdownIt();
const Posts = () => { const Posts = () => {
const [posts, setPosts] = useState([]); const [posts, setPosts] = useState([]);
const [text, setText] = useState(''); const [text, setText] = useState('');
const [media, setMedia] = useState('');
const [mediaType, setMediaType] = useState('');
const [code, setCode] = useState('');
const axiosInstance = axios.create({ const axiosInstance = axios.create({
baseURL: 'http://localhost:3000', baseURL: 'http://localhost:3000',
@ -31,45 +35,28 @@ const Posts = () => {
const handleSubmit = async (e) => { const handleSubmit = async (e) => {
e.preventDefault(); e.preventDefault();
try { try {
const newPost = { text, media, mediaType, code }; const newPost = { text };
await axiosInstance.post('/posts', newPost); await axiosInstance.post('/posts', newPost);
fetchPosts(); // Обновляем список постов после создания нового fetchPosts(); // Обновляем список постов после создания нового
setText(''); setText('');
setMedia('');
setMediaType('');
setCode('');
} catch (error) { } catch (error) {
console.error('Error creating post:', error); console.error('Error creating post:', error);
} }
}; };
const handleEditorChange = ({ text }) => {
setText(text);
};
return ( return (
<div> <div>
<h1>Posts</h1> <h1>Posts</h1>
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
<input <MarkdownEditor
type="text"
placeholder="Text"
value={text} value={text}
onChange={(e) => setText(e.target.value)} style={{ height: '300px' }}
/> renderHTML={(text) => mdParser.render(text)}
<input onChange={handleEditorChange}
type="text"
placeholder="Media URL"
value={media}
onChange={(e) => setMedia(e.target.value)}
/>
<input
type="text"
placeholder="Media Type"
value={mediaType}
onChange={(e) => setMediaType(e.target.value)}
/>
<input
type="text"
placeholder="Code"
value={code}
onChange={(e) => setCode(e.target.value)}
/> />
<button type="submit">Create Post</button> <button type="submit">Create Post</button>
</form> </form>
@ -77,10 +64,7 @@ const Posts = () => {
<ul> <ul>
{posts.map((post, index) => ( {posts.map((post, index) => (
<li key={index}> <li key={index}>
<p>{post.text}</p> <ReactMarkdown>{post.text}</ReactMarkdown>
{post.media && <img src={post.media} alt="Post media" />}
<p>Media Type: {post.mediaType}</p>
<p>Code: {post.code}</p>
</li> </li>
))} ))}
</ul> </ul>