Files
hh-auto-reply/src/openai.ts
Oscar c67fcfe4c6
All checks were successful
Deploy / deploy (push) Successful in 48s
feat(resume): добавлены логи для обработчика резюме
️ Использован createLogger для управления логами в resume.ts. Логи рапперащают о загрузке списка резюме и ошибках.
2026-06-01 11:03:33 +03:00

152 lines
4.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import process from 'node:process'
import { createOpencode, createOpencodeClient } from '@opencode-ai/sdk'
// import Anthropic from '@anthropic-ai/sdk'
import OpenAI from 'openai'
const log = createLogger('llm')
// export const claude = new Anthropic({
// apiKey: process.env.ANTHROPIC_API_KEY,
// })
const OPENCODE_URL = 'http://127.0.0.1:4096'
let _client: ReturnType<typeof createOpencodeClient> | null = null
async function getClient() {
if (_client)
return _client
// Если сервер уже запущен (напр. после хот-релоада) — просто подключаемся
try {
const existing = createOpencodeClient({ baseUrl: OPENCODE_URL })
await existing.session.list()
_client = existing
return _client
}
catch {}
// Иначе стартуем новый сервер
const oc = await createOpencode({
hostname: '127.0.0.1',
port: 4096,
config: {
model: 'openrouter/deepseek/deepseek-v4-flash',
provider: {
openrouter: {
options: { apiKey: process.env.OPENROUTER_API_KEY },
},
},
agent: {
build: { tools: { '*': false } },
},
},
})
_client = oc.client
return _client
}
export const groq = new OpenAI({
apiKey: process.env.GROQ_API_KEY,
baseURL: 'https://api.groq.com/openai/v1',
})
export async function test() {
const client = createOpencodeClient({
baseUrl: 'http://localhost:4096',
})
const test = await client.config.providers()
log.debug('providers', test.data)
}
export async function askLLM(userMessage: string) {
const client = await getClient()
log.info('askLLM called')
// Создаём сессию
const session = await client.session.create({
body: { title: 'My request' },
})
// console.log('session: ', session.data)
const result = await client.session.prompt({
path: { id: session.data!.id },
body: {
parts: [{ type: 'text', text: userMessage }],
},
})
// console.log('result: ', result.data)
const textPart = result.data?.parts?.find((p: { type: string }) => p.type === 'text') as { type: 'text', text: string } | undefined
// console.log(textPart?.text ?? '')
return textPart?.text ?? ''
}
export async function createMessage(resume: string, message: string, prompt?: string) {
const client = await getClient()
log.debug('client.instance:', !!client.instance)
const session = await client.session.create({ body: { title: 'Cover letter' } })
const sessionId = session.data!.id
log.debug('sessionId:', sessionId)
const finalPromt = prompt || 'Ты — помощник по написанию сопроводительных писем. Отвечай только текстом самого письма, без вступлений, ремарок и пояснений. Опирайся на резюме и ничего не выдумывай, чего недостаточно в резюме лучше умолчать. Пиши по короче и простыми словами. В конце письма оставляй все контакты для связи.'
const resumePreview = resume.slice(0, 200).replace(/\n/g, ' ')
log.divider('Prompt 1 — system + resume (noReply)')
log.llm(`system: ${finalPromt.slice(0, 80)}`)
log.llm(`resume: ${resumePreview}`)
await client.session.prompt({
path: { id: sessionId },
body: {
noReply: true,
parts: [{ type: 'text', text: `${finalPromt}\n Резюме:\n${resume}` }],
},
})
const vacancyPreview = message.slice(0, 300).replace(/\n/g, ' ')
log.divider('Prompt 2 — vacancy')
log.llm(`📝 vacancy → ожидаю ответ…`)
log.llm(`vacancy: ${vacancyPreview}`)
// ${prompt}\n\n
const result = await client.session.prompt({
path: { id: sessionId },
body: {
parts: [{ type: 'text', text: `Вакансия:\n${message}` }],
},
})
const parts = (result.data?.parts ?? []) as { type: string, text?: string }[]
const textPart = parts.find(p => p.type === 'text')
log.divider('Ответ получен')
log.llm(`${textPart?.text?.length ?? 0} символов`)
log.llm(`${textPart?.text?.slice(0, 150).replace(/\n/g, ' ') ?? 'null'}`)
try {
await client.session.delete({ path: { id: sessionId } })
}
catch (e) {
log.error('Session cleanup error:', (e as Error).message)
}
return textPart?.text ?? null
}
export async function askGPT(resume: string, message: string, prompt: string) {
// return 'test'
const res = await groq.chat.completions.create({
model: 'llama-3.3-70b-versatile',
messages: [
{ role: 'system', content: `${prompt} ${resume}` },
{ role: 'user', content: message },
],
})
return res.choices[0].message.content
}