fix: prisma + posts service
This commit is contained in:
parent
11082d93a5
commit
435b7f3fca
@ -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
1226
back/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -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",
|
||||||
|
@ -1,18 +1,20 @@
|
|||||||
datasource db {
|
generator client {
|
||||||
provider = "postgresql"
|
provider = "prisma-client-js"
|
||||||
url = env("DATABASE_URL")
|
binaryTargets = ["native", "debian-openssl-3.0.x"]
|
||||||
|
previewFeatures = ["postgresqlExtensions"]
|
||||||
}
|
}
|
||||||
|
|
||||||
generator client {
|
datasource db {
|
||||||
provider = "prisma-client-js"
|
provider = "postgresql"
|
||||||
binaryTargets = ["native", "debian-openssl-3.0.x"]
|
url = env("DATABASE_URL")
|
||||||
|
extensions = [pgvector(map: "vector")]
|
||||||
}
|
}
|
||||||
|
|
||||||
model Post {
|
model Post {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
text String?
|
text String?
|
||||||
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")?
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,13 +1,32 @@
|
|||||||
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
1169
front/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -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"
|
||||||
},
|
},
|
||||||
|
@ -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>
|
||||||
@ -88,4 +72,4 @@ const Posts = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Posts;
|
export default Posts;
|
Loading…
Reference in New Issue
Block a user