-
-
Notifications
You must be signed in to change notification settings - Fork 190
/
Copy pathgithubmodels.lua
229 lines (216 loc) · 7.29 KB
/
githubmodels.lua
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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
local Job = require("plenary.job")
local log = require("codecompanion.utils.log")
local openai = require("codecompanion.adapters.openai")
local utils = require("codecompanion.utils.adapters")
---@alias GhToken string|nil
local _gh_token
local function get_github_token()
local token
local job = Job:new({
command = "gh",
args = { "auth", "token", "-h", "github.com" },
on_exit = function(j, return_val)
if return_val == 0 then
token = j:result()[1]
end
end,
})
job:sync()
return token
end
---Authorize the GitHub OAuth token
---@return GhToken
local function authorize_token()
if _gh_token then
log:debug("Reusing gh cli token")
return _gh_token
end
log:debug("Getting gh cli token")
_gh_token = get_github_token()
return _gh_token
end
---@class GitHubModels.Adapter: CodeCompanion.Adapter
return {
name = "githubmodels",
formatted_name = "GitHub Models",
roles = {
llm = "assistant",
user = "user",
},
opts = {
stream = true,
},
features = {
text = true,
tokens = true,
vision = false,
},
url = "https://models.inference.ai.azure.com/chat/completions",
env = {
---@return string|nil
api_key = function()
return authorize_token()
end,
},
headers = {
Authorization = "Bearer ${api_key}",
["Content-Type"] = "application/json",
-- Idea below taken from : https://github.com/github/gh-models/blob/d3b8d3e1d4c5a412e9af09a43a42eb365dac5751/internal/azuremodels/azure_client.go#L69
-- Azure would like us to send specific user agents to help distinguish
-- traffic from known sources and other web requests
-- send both to accommodate various Azure consumers
["x-ms-useragent"] = "Neovim/" .. vim.version().major .. "." .. vim.version().minor .. "." .. vim.version().patch,
["x-ms-user-agent"] = "Neovim/" .. vim.version().major .. "." .. vim.version().minor .. "." .. vim.version().patch,
},
handlers = {
---Check for a token before starting the request
---@param self CodeCompanion.Adapter
---@return boolean
setup = function(self)
local model = self.schema.model.default
local model_opts = self.schema.model.choices[model]
if model_opts and model_opts.opts then
self.opts = vim.tbl_deep_extend("force", self.opts, model_opts.opts)
end
if self.opts and self.opts.stream then
self.parameters.stream = true
end
_gh_token = authorize_token()
if not _gh_token then
log:error("GitHub Models Adapter: Could not authorize your GitHub token")
return false
end
return true
end,
--- Use the OpenAI adapter for the bulk of the work
form_parameters = function(self, params, messages)
return openai.handlers.form_parameters(self, params, messages)
end,
form_messages = function(self, messages)
return openai.handlers.form_messages(self, messages)
end,
tokens = function(self, data)
if data and data ~= "" then
local data_mod = utils.clean_streamed_data(data)
local ok, json = pcall(vim.json.decode, data_mod, { luanil = { object = true } })
if ok then
if json.usage then
local total_tokens = json.usage.total_tokens or 0
local completion_tokens = json.usage.completion_tokens or 0
local prompt_tokens = json.usage.prompt_tokens or 0
local tokens = total_tokens > 0 and total_tokens or completion_tokens + prompt_tokens
log:trace("Tokens: %s", tokens)
return tokens
end
end
end
end,
chat_output = function(self, data)
return openai.handlers.chat_output(self, data)
end,
inline_output = function(self, data, context)
return openai.handlers.inline_output(self, data, context)
end,
on_exit = function(self, data)
return openai.handlers.on_exit(self, data)
end,
},
schema = {
---@type CodeCompanion.Schema
model = {
order = 1,
mapping = "parameters",
type = "enum",
desc = "ID of the model to use. See the model endpoint compatibility table for details on which models work with the Chat API.",
---@type string|fun(): string
default = "gpt-4o",
choices = {
["o3-mini"] = { opts = { can_reason = true } },
["o1"] = { opts = { can_reason = true } },
["o1-mini"] = { opts = { can_reason = true } },
"claude-3.5-sonnet",
"gpt-4o",
"gpt-4o-mini",
"DeepSeek-R1",
"Codestral-2501",
},
},
---@type CodeCompanion.Schema
reasoning_effort = {
order = 2,
mapping = "parameters",
type = "string",
optional = true,
condition = function(self)
local model = self.schema.model.default
if type(model) == "function" then
model = model()
end
if self.schema.model.choices[model] and self.schema.model.choices[model].opts then
return self.schema.model.choices[model].opts.can_reason
end
return false
end,
default = "medium",
desc = "Constrains effort on reasoning for reasoning models. Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning in a response.",
choices = {
"high",
"medium",
"low",
},
},
---@type CodeCompanion.Schema
temperature = {
order = 3,
mapping = "parameters",
type = "number",
default = 0,
condition = function(self)
local model = self.schema.model.default
if type(model) == "function" then
model = model()
end
return not vim.startswith(model, "o1")
end,
desc = "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. We generally recommend altering this or top_p but not both.",
},
---@type CodeCompanion.Schema
max_tokens = {
order = 4,
mapping = "parameters",
type = "integer",
default = 4096,
desc = "The maximum number of tokens to generate in the chat completion. The total length of input tokens and generated tokens is limited by the model's context length.",
},
---@type CodeCompanion.Schema
top_p = {
order = 5,
mapping = "parameters",
type = "number",
default = 1,
condition = function(self)
local model = self.schema.model.default
if type(model) == "function" then
model = model()
end
return not vim.startswith(model, "o1")
end,
desc = "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both.",
},
---@type CodeCompanion.Schema
n = {
order = 6,
mapping = "parameters",
type = "number",
default = 1,
condition = function(self)
local model = self.schema.model.default
if type(model) == "function" then
model = model()
end
return not vim.startswith(model, "o1")
end,
desc = "How many chat completions to generate for each prompt.",
},
},
}