File size: 4,930 Bytes
cb2bc0c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import express from 'express'
import cors from 'cors'
import fetch from 'node-fetch'

const app = express()

// Считываем ключ Helix из переменных окружения
const HELIX_API_KEY = process.env.HELIX_API_KEY || ''

// CORS
app.use(cors({
  origin: '*',
  methods: ['GET', 'POST', 'OPTIONS'],
  allowedHeaders: ['Content-Type', 'Authorization']
}))
app.use(express.json())
app.options('*', (req, res) => res.sendStatus(204))

// Список доступных моделей
app.get(['/models', '/v1/models'], (req, res) => {
  res.json({
    object: 'list',
    data: [
      // OpenAI — GPT‑4.1 / 4.5
      { id: 'gpt-4.1',                    object: 'model', created: 0, owned_by: 'helix' },
      { id: 'gpt-4.1-2025-04-14',         object: 'model', created: 0, owned_by: 'helix' },
      { id: 'gpt-4.1-mini',               object: 'model', created: 0, owned_by: 'helix' },
      { id: 'gpt-4.1-mini-2025-04-14',    object: 'model', created: 0, owned_by: 'helix' },
      { id: 'gpt-4.1-nano',               object: 'model', created: 0, owned_by: 'helix' },
      { id: 'gpt-4.1-nano-2025-04-14',    object: 'model', created: 0, owned_by: 'helix' },
      { id: 'gpt-4.5-preview',            object: 'model', created: 0, owned_by: 'helix' },
      { id: 'gpt-4.5-preview-2025-02-27', object: 'model', created: 0, owned_by: 'helix' },

      // OpenAI — GPT‑4o
      { id: 'gpt-4o',                           object: 'model', created: 0, owned_by: 'helix' },
      { id: 'gpt-4o-2024-05-13',                object: 'model', created: 0, owned_by: 'helix' },
      { id: 'gpt-4o-2024-08-06',                object: 'model', created: 0, owned_by: 'helix' },
      { id: 'gpt-4o-2024-11-20',                object: 'model', created: 0, owned_by: 'helix' },
      { id: 'gpt-4o-mini',                      object: 'model', created: 0, owned_by: 'helix' },
      { id: 'gpt-4o-mini-2024-07-18',           object: 'model', created: 0, owned_by: 'helix' },
      { id: 'gpt-4o-search-preview',            object: 'model', created: 0, owned_by: 'helix' },
      { id: 'gpt-4o-search-preview-2025-03-11', object: 'model', created: 0, owned_by: 'helix' },
      { id: 'gpt-4o-mini-search-preview',       object: 'model', created: 0, owned_by: 'helix' },
      { id: 'gpt-4o-mini-search-preview-2025-03-11', object: 'model', created: 0, owned_by: 'helix' },

      // Helix — GPT‑3.5 Turbo
      { id: 'gpt-3.5-turbo', object: 'model', created: 0, owned_by: 'helix' }
    ]
  })
})

// Прокси для /chat/completions
app.post(['/chat/completions', '/v1/chat/completions'], async (req, res) => {
  const {
    model,
    messages = [],
    stream = false,
    temperature,
    top_p,
    presence_penalty,
    frequency_penalty,
    ...rest
  } = req.body

  // Собираем историю, заменяя китайские префиксы на English
  const historyText = messages
    .map(m => (m.role === 'user' ? 'User: ' : 'Assistant: ') + m.content)
    .join('\n')

  // Формируем полезную нагрузку для Helix
  const helixPayload = {
    type: 'text',
    stream,
    provider: getProvider(model),
    model,
    messages: [
      {
        role: 'user',
        content: { content_type: 'text', parts: [historyText] }
      }
    ],
    temperature,
    top_p,
    presence_penalty,
    frequency_penalty,
    ...rest
  }

  // Заголовок авторизации для Helix
  const authHeader = HELIX_API_KEY
    ? `Bearer ${HELIX_API_KEY}`
    : (req.header('authorization') || '')

  // Отправляем запрос в Helix
  const helixRes = await fetch(
    'https://app.tryhelix.ai/api/v1/sessions/chat',
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Authorization: authHeader
      },
      body: JSON.stringify(helixPayload)
    }
  )

  if (!stream) {
    const data = await helixRes.json()
    const reply = data?.choices?.[0]?.message?.content ?? ''
    return res.status(helixRes.status).json({
      id: `chatcmpl-proxy-${data.id ?? Date.now()}`,
      object: 'chat.completion',
      created: Math.floor(Date.now() / 1000),
      model,
      choices: [
        {
          index: 0,
          message: { role: 'assistant', content: reply },
          finish_reason: 'stop'
        }
      ]
    })
  }

  // При стриме прокидываем SSE напрямую
  res.status(helixRes.status)
  res.set('Content-Type', 'text/event-stream')
  helixRes.body.pipe(res)
})

// Определяем провайдера по имени модели
function getProvider(modelId) {
  if (/^gpt-[34]|^gpt-3\.5/.test(modelId)) return 'openai'
  if (/^(llama|phi|aya|gemma|deepseek|qwen)/.test(modelId)) return 'helix'
  return 'togetherai'
}

const PORT = process.env.PORT || 7860
app.listen(PORT, () => {
  console.log(`🚀 Server listening on port ${PORT}`)
})