Skip to content

Commit b70c21e

Browse files
committed
Merge branch 'hotfix/0.35.5'
2 parents 02f0f38 + 8110127 commit b70c21e

39 files changed

+954
-61
lines changed

Copilot for Xcode.xcodeproj/project.pbxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -958,6 +958,7 @@
958958
ENABLE_PREVIEWS = YES;
959959
GENERATE_INFOPLIST_FILE = YES;
960960
INFOPLIST_FILE = "Copilot-for-Xcode-Info.plist";
961+
INFOPLIST_KEY_CFBundleDisplayName = "";
961962
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
962963
INFOPLIST_KEY_NSHumanReadableCopyright = "";
963964
LD_RUNPATH_SEARCH_PATHS = (
@@ -991,6 +992,7 @@
991992
ENABLE_PREVIEWS = YES;
992993
GENERATE_INFOPLIST_FILE = YES;
993994
INFOPLIST_FILE = "Copilot-for-Xcode-Info.plist";
995+
INFOPLIST_KEY_CFBundleDisplayName = "";
994996
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
995997
INFOPLIST_KEY_NSHumanReadableCopyright = "";
996998
LD_RUNPATH_SEARCH_PATHS = (

Copilot-for-Xcode-Info.plist

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@
1212
<string>$(EXTENSION_BUNDLE_NAME)</string>
1313
<key>HOST_APP_NAME</key>
1414
<string>$(HOST_APP_NAME)</string>
15+
<key>NSAppTransportSecurity</key>
16+
<dict>
17+
<key>NSAllowsArbitraryLoads</key>
18+
<true/>
19+
</dict>
1520
<key>SUEnableJavaScript</key>
1621
<string>YES</string>
1722
<key>SUFeedURL</key>

Core/Sources/ChatGPTChatTab/ChatContextMenu.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ struct ChatContextMenu: View {
7272
var chatModel: some View {
7373
let allModels = chatModels + [.init(
7474
id: "com.github.copilot",
75-
name: "GitHub Copilot as chat model",
75+
name: "GitHub Copilot Language Server",
7676
format: .openAI,
7777
info: .init()
7878
)]

Core/Sources/ChatService/AllPlugins.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ final class LegacyChatPluginWrapper<Plugin: ChatPlugin>: LegacyChatPlugin {
9494
break
9595
case .startNewMessage:
9696
break
97+
case .reasoning:
98+
break
9799
}
98100

99101
await chatGPTService.memory.mutateHistory { history in

Core/Sources/HostApp/AccountSettings/ChatModelManagement/ChatModelEdit.swift

Lines changed: 82 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ struct ChatModelEdit {
3333
var openAIProjectID: String = ""
3434
var customHeaders: [ChatModel.Info.CustomHeaderInfo.HeaderField] = []
3535
var openAICompatibleSupportsMultipartMessageContent = true
36+
var requiresBeginWithUserMessage = false
3637
}
3738

3839
enum Action: Equatable, BindableAction {
@@ -45,10 +46,44 @@ struct ChatModelEdit {
4546
case testSucceeded(String)
4647
case testFailed(String)
4748
case checkSuggestedMaxTokens
49+
case selectModelFormat(ModelFormat)
4850
case apiKeySelection(APIKeySelection.Action)
4951
case baseURLSelection(BaseURLSelection.Action)
5052
}
5153

54+
enum ModelFormat: CaseIterable {
55+
case openAI
56+
case azureOpenAI
57+
case googleAI
58+
case ollama
59+
case claude
60+
case gitHubCopilot
61+
case openAICompatible
62+
case deepSeekOpenAICompatible
63+
case openRouterOpenAICompatible
64+
case grokOpenAICompatible
65+
case mistralOpenAICompatible
66+
67+
init(_ format: ChatModel.Format) {
68+
switch format {
69+
case .openAI:
70+
self = .openAI
71+
case .azureOpenAI:
72+
self = .azureOpenAI
73+
case .googleAI:
74+
self = .googleAI
75+
case .ollama:
76+
self = .ollama
77+
case .claude:
78+
self = .claude
79+
case .openAICompatible:
80+
self = .openAICompatible
81+
case .gitHubCopilot:
82+
self = .gitHubCopilot
83+
}
84+
}
85+
}
86+
5287
var toast: (String, ToastType) -> Void {
5388
@Dependency(\.namespacedToast) var toast
5489
return {
@@ -164,11 +199,53 @@ struct ChatModelEdit {
164199
state.suggestedMaxTokens = nil
165200
}
166201
return .none
202+
case .gitHubCopilot:
203+
if let knownModel = AvailableGitHubCopilotModel(rawValue: state.modelName) {
204+
state.suggestedMaxTokens = knownModel.contextWindow
205+
} else {
206+
state.suggestedMaxTokens = nil
207+
}
208+
return .none
167209
default:
168210
state.suggestedMaxTokens = nil
169211
return .none
170212
}
171213

214+
case let .selectModelFormat(format):
215+
switch format {
216+
case .openAI:
217+
state.format = .openAI
218+
case .azureOpenAI:
219+
state.format = .azureOpenAI
220+
case .googleAI:
221+
state.format = .googleAI
222+
case .ollama:
223+
state.format = .ollama
224+
case .claude:
225+
state.format = .claude
226+
case .gitHubCopilot:
227+
state.format = .gitHubCopilot
228+
case .openAICompatible:
229+
state.format = .openAICompatible
230+
case .deepSeekOpenAICompatible:
231+
state.format = .openAICompatible
232+
state.baseURLSelection.baseURL = "https://api.deepseek.com"
233+
state.baseURLSelection.isFullURL = false
234+
case .openRouterOpenAICompatible:
235+
state.format = .openAICompatible
236+
state.baseURLSelection.baseURL = "https://openrouter.ai"
237+
state.baseURLSelection.isFullURL = false
238+
case .grokOpenAICompatible:
239+
state.format = .openAICompatible
240+
state.baseURLSelection.baseURL = "https://api.x.ai"
241+
state.baseURLSelection.isFullURL = false
242+
case .mistralOpenAICompatible:
243+
state.format = .openAICompatible
244+
state.baseURLSelection.baseURL = "https://api.mistral.ai"
245+
state.baseURLSelection.isFullURL = false
246+
}
247+
return .none
248+
172249
case .apiKeySelection:
173250
return .none
174251

@@ -208,7 +285,7 @@ extension ChatModel {
208285
switch state.format {
209286
case .googleAI, .ollama, .claude:
210287
return false
211-
case .azureOpenAI, .openAI, .openAICompatible:
288+
case .azureOpenAI, .openAI, .openAICompatible, .gitHubCopilot:
212289
return state.supportsFunctionCalling
213290
}
214291
}(),
@@ -222,7 +299,8 @@ extension ChatModel {
222299
openAICompatibleInfo: .init(
223300
enforceMessageOrder: state.enforceMessageOrder,
224301
supportsMultipartMessageContent: state
225-
.openAICompatibleSupportsMultipartMessageContent
302+
.openAICompatibleSupportsMultipartMessageContent,
303+
requiresBeginWithUserMessage: state.requiresBeginWithUserMessage
226304
),
227305
customHeaderInfo: .init(headers: state.customHeaders)
228306
)
@@ -249,7 +327,8 @@ extension ChatModel {
249327
openAIProjectID: info.openAIInfo.projectID,
250328
customHeaders: info.customHeaderInfo.headers,
251329
openAICompatibleSupportsMultipartMessageContent: info.openAICompatibleInfo
252-
.supportsMultipartMessageContent
330+
.supportsMultipartMessageContent,
331+
requiresBeginWithUserMessage: info.openAICompatibleInfo.requiresBeginWithUserMessage
253332
)
254333
}
255334
}

Core/Sources/HostApp/AccountSettings/ChatModelManagement/ChatModelEditView.swift

Lines changed: 97 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ struct ChatModelEditView: View {
2929
OllamaForm(store: store)
3030
case .claude:
3131
ClaudeForm(store: store)
32+
case .gitHubCopilot:
33+
GitHubCopilotForm(store: store)
3234
}
3335
}
3436
.padding()
@@ -86,31 +88,44 @@ struct ChatModelEditView: View {
8688
var body: some View {
8789
WithPerceptionTracking {
8890
Picker(
89-
selection: $store.format,
91+
selection: Binding(
92+
get: { .init(store.format) },
93+
set: { store.send(.selectModelFormat($0)) }
94+
),
9095
content: {
9196
ForEach(
92-
ChatModel.Format.allCases,
93-
id: \.rawValue
97+
ChatModelEdit.ModelFormat.allCases,
98+
id: \.self
9499
) { format in
95100
switch format {
96101
case .openAI:
97-
Text("OpenAI").tag(format)
102+
Text("OpenAI")
98103
case .azureOpenAI:
99-
Text("Azure OpenAI").tag(format)
104+
Text("Azure OpenAI")
100105
case .openAICompatible:
101-
Text("OpenAI Compatible").tag(format)
106+
Text("OpenAI Compatible")
102107
case .googleAI:
103-
Text("Google Generative AI").tag(format)
108+
Text("Google AI")
104109
case .ollama:
105-
Text("Ollama").tag(format)
110+
Text("Ollama")
106111
case .claude:
107-
Text("Claude").tag(format)
112+
Text("Claude")
113+
case .gitHubCopilot:
114+
Text("GitHub Copilot")
115+
case .deepSeekOpenAICompatible:
116+
Text("DeepSeek (OpenAI Compatible)")
117+
case .openRouterOpenAICompatible:
118+
Text("OpenRouter (OpenAI Compatible)")
119+
case .grokOpenAICompatible:
120+
Text("Grok (OpenAI Compatible)")
121+
case .mistralOpenAICompatible:
122+
Text("Mistral (OpenAI Compatible)")
108123
}
109124
}
110125
},
111126
label: { Text("Format") }
112127
)
113-
.pickerStyle(.segmented)
128+
.pickerStyle(.menu)
114129
}
115130
}
116131
}
@@ -243,7 +258,7 @@ struct ChatModelEditView: View {
243258

244259
MaxTokensTextField(store: store)
245260
SupportsFunctionCallingToggle(store: store)
246-
261+
247262
TextField(text: $store.openAIOrganizationID, prompt: Text("Optional")) {
248263
Text("Organization ID")
249264
}
@@ -321,11 +336,15 @@ struct ChatModelEditView: View {
321336
Toggle(isOn: $store.enforceMessageOrder) {
322337
Text("Enforce message order to be user/assistant alternated")
323338
}
324-
339+
325340
Toggle(isOn: $store.openAICompatibleSupportsMultipartMessageContent) {
326341
Text("Support multi-part message content")
327342
}
328343

344+
Toggle(isOn: $store.requiresBeginWithUserMessage) {
345+
Text("Requires the first message to be from the user")
346+
}
347+
329348
Button("Custom Headers") {
330349
isEditingCustomHeader.toggle()
331350
}
@@ -375,12 +394,16 @@ struct ChatModelEditView: View {
375394

376395
struct OllamaForm: View {
377396
@Perception.Bindable var store: StoreOf<ChatModelEdit>
397+
@State var isEditingCustomHeader = false
398+
378399
var body: some View {
379400
WithPerceptionTracking {
380401
BaseURLTextField(store: store, prompt: Text("http://127.0.0.1:11434")) {
381402
Text("/api/chat")
382403
}
383404

405+
ApiKeyNamePicker(store: store)
406+
384407
TextField("Model Name", text: $store.modelName)
385408

386409
MaxTokensTextField(store: store)
@@ -389,12 +412,19 @@ struct ChatModelEditView: View {
389412
Text("Keep Alive")
390413
}
391414

415+
Button("Custom Headers") {
416+
isEditingCustomHeader.toggle()
417+
}
418+
392419
VStack(alignment: .leading, spacing: 8) {
393420
Text(Image(systemName: "exclamationmark.triangle.fill")) + Text(
394421
" For more details, please visit [https://ollama.com](https://ollama.com)."
395422
)
396423
}
397424
.padding(.vertical)
425+
426+
}.sheet(isPresented: $isEditingCustomHeader) {
427+
CustomHeaderSettingsView(headers: $store.customHeaders)
398428
}
399429
}
400430
}
@@ -442,6 +472,61 @@ struct ChatModelEditView: View {
442472
}
443473
}
444474
}
475+
476+
struct GitHubCopilotForm: View {
477+
@Perception.Bindable var store: StoreOf<ChatModelEdit>
478+
@State var isEditingCustomHeader = false
479+
480+
var body: some View {
481+
WithPerceptionTracking {
482+
TextField("Model Name", text: $store.modelName)
483+
.overlay(alignment: .trailing) {
484+
Picker(
485+
"",
486+
selection: $store.modelName,
487+
content: {
488+
if AvailableGitHubCopilotModel(rawValue: store.modelName) == nil {
489+
Text("Custom Model").tag(store.modelName)
490+
}
491+
ForEach(AvailableGitHubCopilotModel.allCases, id: \.self) { model in
492+
Text(model.rawValue).tag(model.rawValue)
493+
}
494+
}
495+
)
496+
.frame(width: 20)
497+
}
498+
499+
MaxTokensTextField(store: store)
500+
SupportsFunctionCallingToggle(store: store)
501+
502+
Toggle(isOn: $store.enforceMessageOrder) {
503+
Text("Enforce message order to be user/assistant alternated")
504+
}
505+
506+
Toggle(isOn: $store.openAICompatibleSupportsMultipartMessageContent) {
507+
Text("Support multi-part message content")
508+
}
509+
510+
Button("Custom Headers") {
511+
isEditingCustomHeader.toggle()
512+
}
513+
514+
VStack(alignment: .leading, spacing: 8) {
515+
Text(Image(systemName: "exclamationmark.triangle.fill")) + Text(
516+
" Please login in the GitHub Copilot settings to use the model."
517+
)
518+
519+
Text(Image(systemName: "exclamationmark.triangle.fill")) + Text(
520+
" This will call the APIs directly, which may not be allowed by GitHub. But it's used in other popular apps like Zed."
521+
)
522+
}
523+
.dynamicHeightTextInFormWorkaround()
524+
.padding(.vertical)
525+
}.sheet(isPresented: $isEditingCustomHeader) {
526+
CustomHeaderSettingsView(headers: $store.customHeaders)
527+
}
528+
}
529+
}
445530
}
446531

447532
#Preview("OpenAI") {

Core/Sources/HostApp/AccountSettings/ChatModelManagement/ChatModelManagement.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ extension ChatModel: ManageableAIModel {
1313
case .googleAI: return "Google Generative AI"
1414
case .ollama: return "Ollama"
1515
case .claude: return "Claude"
16+
case .gitHubCopilot: return "GitHub Copilot"
1617
}
1718
}
1819

0 commit comments

Comments
 (0)