@ottocode/sdk 0.1.224 → 0.1.226
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ottocode/sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.226",
|
|
4
4
|
"description": "AI agent SDK for building intelligent assistants - tree-shakable and comprehensive",
|
|
5
5
|
"author": "nitishxyz",
|
|
6
6
|
"license": "MIT",
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
"@modelcontextprotocol/sdk": "^1.12",
|
|
102
102
|
"@openauthjs/openauth": "^0.4.3",
|
|
103
103
|
"@openrouter/ai-sdk-provider": "^1.2.0",
|
|
104
|
-
"@ottocode/ai-sdk": "0.
|
|
104
|
+
"@ottocode/ai-sdk": "0.2.0",
|
|
105
105
|
"@solana/web3.js": "^1.98.0",
|
|
106
106
|
"ai": "^6.0.0",
|
|
107
107
|
"bs58": "^6.0.0",
|
|
@@ -757,6 +757,57 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
|
|
|
757
757
|
output: 32000,
|
|
758
758
|
},
|
|
759
759
|
},
|
|
760
|
+
{
|
|
761
|
+
id: 'gpt-5.4',
|
|
762
|
+
ownedBy: 'openai',
|
|
763
|
+
label: 'GPT-5.4',
|
|
764
|
+
modalities: {
|
|
765
|
+
input: ['text', 'image'],
|
|
766
|
+
output: ['text'],
|
|
767
|
+
},
|
|
768
|
+
toolCall: true,
|
|
769
|
+
reasoningText: true,
|
|
770
|
+
attachment: true,
|
|
771
|
+
temperature: false,
|
|
772
|
+
knowledge: '2025-08-31',
|
|
773
|
+
releaseDate: '2026-03-05',
|
|
774
|
+
lastUpdated: '2026-03-05',
|
|
775
|
+
openWeights: false,
|
|
776
|
+
cost: {
|
|
777
|
+
input: 2.5,
|
|
778
|
+
output: 15,
|
|
779
|
+
cacheRead: 0.25,
|
|
780
|
+
},
|
|
781
|
+
limit: {
|
|
782
|
+
context: 1050000,
|
|
783
|
+
output: 128000,
|
|
784
|
+
},
|
|
785
|
+
},
|
|
786
|
+
{
|
|
787
|
+
id: 'gpt-5.4-pro',
|
|
788
|
+
ownedBy: 'openai',
|
|
789
|
+
label: 'GPT-5.4 Pro',
|
|
790
|
+
modalities: {
|
|
791
|
+
input: ['text', 'image'],
|
|
792
|
+
output: ['text'],
|
|
793
|
+
},
|
|
794
|
+
toolCall: true,
|
|
795
|
+
reasoningText: true,
|
|
796
|
+
attachment: true,
|
|
797
|
+
temperature: false,
|
|
798
|
+
knowledge: '2025-08-31',
|
|
799
|
+
releaseDate: '2026-03-05',
|
|
800
|
+
lastUpdated: '2026-03-05',
|
|
801
|
+
openWeights: false,
|
|
802
|
+
cost: {
|
|
803
|
+
input: 30,
|
|
804
|
+
output: 180,
|
|
805
|
+
},
|
|
806
|
+
limit: {
|
|
807
|
+
context: 1050000,
|
|
808
|
+
output: 128000,
|
|
809
|
+
},
|
|
810
|
+
},
|
|
760
811
|
{
|
|
761
812
|
id: 'o1',
|
|
762
813
|
ownedBy: 'openai',
|
|
@@ -3614,6 +3665,31 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
|
|
|
3614
3665
|
output: 65536,
|
|
3615
3666
|
},
|
|
3616
3667
|
},
|
|
3668
|
+
{
|
|
3669
|
+
id: 'google/gemini-3.1-pro-preview-customtools',
|
|
3670
|
+
ownedBy: 'google',
|
|
3671
|
+
label: 'Gemini 3.1 Pro Preview Custom Tools',
|
|
3672
|
+
modalities: {
|
|
3673
|
+
input: ['text', 'image', 'audio', 'video', 'pdf'],
|
|
3674
|
+
output: ['text'],
|
|
3675
|
+
},
|
|
3676
|
+
toolCall: true,
|
|
3677
|
+
reasoningText: true,
|
|
3678
|
+
attachment: true,
|
|
3679
|
+
temperature: true,
|
|
3680
|
+
knowledge: '2025-01',
|
|
3681
|
+
releaseDate: '2026-02-19',
|
|
3682
|
+
lastUpdated: '2026-02-19',
|
|
3683
|
+
openWeights: false,
|
|
3684
|
+
cost: {
|
|
3685
|
+
input: 2,
|
|
3686
|
+
output: 12,
|
|
3687
|
+
},
|
|
3688
|
+
limit: {
|
|
3689
|
+
context: 1048576,
|
|
3690
|
+
output: 65536,
|
|
3691
|
+
},
|
|
3692
|
+
},
|
|
3617
3693
|
{
|
|
3618
3694
|
id: 'google/gemma-2-9b-it',
|
|
3619
3695
|
ownedBy: 'google',
|
|
@@ -5396,6 +5472,84 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
|
|
|
5396
5472
|
output: 128000,
|
|
5397
5473
|
},
|
|
5398
5474
|
},
|
|
5475
|
+
{
|
|
5476
|
+
id: 'openai/gpt-5.3-codex',
|
|
5477
|
+
ownedBy: 'openai',
|
|
5478
|
+
label: 'GPT-5.3-Codex',
|
|
5479
|
+
modalities: {
|
|
5480
|
+
input: ['text', 'image', 'pdf'],
|
|
5481
|
+
output: ['text'],
|
|
5482
|
+
},
|
|
5483
|
+
toolCall: true,
|
|
5484
|
+
reasoningText: true,
|
|
5485
|
+
attachment: true,
|
|
5486
|
+
temperature: false,
|
|
5487
|
+
knowledge: '2025-08-31',
|
|
5488
|
+
releaseDate: '2026-02-24',
|
|
5489
|
+
lastUpdated: '2026-02-24',
|
|
5490
|
+
openWeights: false,
|
|
5491
|
+
cost: {
|
|
5492
|
+
input: 1.75,
|
|
5493
|
+
output: 14,
|
|
5494
|
+
cacheRead: 0.175,
|
|
5495
|
+
},
|
|
5496
|
+
limit: {
|
|
5497
|
+
context: 400000,
|
|
5498
|
+
output: 128000,
|
|
5499
|
+
},
|
|
5500
|
+
},
|
|
5501
|
+
{
|
|
5502
|
+
id: 'openai/gpt-5.4',
|
|
5503
|
+
ownedBy: 'openai',
|
|
5504
|
+
label: 'GPT-5.4',
|
|
5505
|
+
modalities: {
|
|
5506
|
+
input: ['text', 'image', 'pdf'],
|
|
5507
|
+
output: ['text'],
|
|
5508
|
+
},
|
|
5509
|
+
toolCall: true,
|
|
5510
|
+
reasoningText: true,
|
|
5511
|
+
attachment: true,
|
|
5512
|
+
temperature: false,
|
|
5513
|
+
knowledge: '2025-08-31',
|
|
5514
|
+
releaseDate: '2026-03-05',
|
|
5515
|
+
lastUpdated: '2026-03-05',
|
|
5516
|
+
openWeights: false,
|
|
5517
|
+
cost: {
|
|
5518
|
+
input: 2.5,
|
|
5519
|
+
output: 15,
|
|
5520
|
+
cacheRead: 0.25,
|
|
5521
|
+
},
|
|
5522
|
+
limit: {
|
|
5523
|
+
context: 1050000,
|
|
5524
|
+
output: 128000,
|
|
5525
|
+
},
|
|
5526
|
+
},
|
|
5527
|
+
{
|
|
5528
|
+
id: 'openai/gpt-5.4-pro',
|
|
5529
|
+
ownedBy: 'openai',
|
|
5530
|
+
label: 'GPT-5.4 Pro',
|
|
5531
|
+
modalities: {
|
|
5532
|
+
input: ['text', 'image', 'pdf'],
|
|
5533
|
+
output: ['text'],
|
|
5534
|
+
},
|
|
5535
|
+
toolCall: true,
|
|
5536
|
+
reasoningText: true,
|
|
5537
|
+
attachment: true,
|
|
5538
|
+
temperature: false,
|
|
5539
|
+
knowledge: '2025-08-31',
|
|
5540
|
+
releaseDate: '2026-03-05',
|
|
5541
|
+
lastUpdated: '2026-03-05',
|
|
5542
|
+
openWeights: false,
|
|
5543
|
+
cost: {
|
|
5544
|
+
input: 30,
|
|
5545
|
+
output: 180,
|
|
5546
|
+
cacheRead: 30,
|
|
5547
|
+
},
|
|
5548
|
+
limit: {
|
|
5549
|
+
context: 1050000,
|
|
5550
|
+
output: 128000,
|
|
5551
|
+
},
|
|
5552
|
+
},
|
|
5399
5553
|
{
|
|
5400
5554
|
id: 'openai/gpt-oss-120b',
|
|
5401
5555
|
ownedBy: 'openai',
|
|
@@ -7799,6 +7953,122 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
|
|
|
7799
7953
|
npm: '@ai-sdk/openai',
|
|
7800
7954
|
},
|
|
7801
7955
|
},
|
|
7956
|
+
{
|
|
7957
|
+
id: 'gpt-5.3-codex',
|
|
7958
|
+
ownedBy: 'openai',
|
|
7959
|
+
label: 'GPT-5.3 Codex',
|
|
7960
|
+
modalities: {
|
|
7961
|
+
input: ['text', 'image', 'pdf'],
|
|
7962
|
+
output: ['text'],
|
|
7963
|
+
},
|
|
7964
|
+
toolCall: true,
|
|
7965
|
+
reasoningText: true,
|
|
7966
|
+
attachment: true,
|
|
7967
|
+
temperature: false,
|
|
7968
|
+
knowledge: '2025-08-31',
|
|
7969
|
+
releaseDate: '2026-02-24',
|
|
7970
|
+
lastUpdated: '2026-02-24',
|
|
7971
|
+
openWeights: false,
|
|
7972
|
+
cost: {
|
|
7973
|
+
input: 1.75,
|
|
7974
|
+
output: 14,
|
|
7975
|
+
cacheRead: 0.175,
|
|
7976
|
+
},
|
|
7977
|
+
limit: {
|
|
7978
|
+
context: 400000,
|
|
7979
|
+
output: 128000,
|
|
7980
|
+
},
|
|
7981
|
+
provider: {
|
|
7982
|
+
npm: '@ai-sdk/openai',
|
|
7983
|
+
},
|
|
7984
|
+
},
|
|
7985
|
+
{
|
|
7986
|
+
id: 'gpt-5.3-codex-spark',
|
|
7987
|
+
ownedBy: 'openai',
|
|
7988
|
+
label: 'GPT-5.3 Codex Spark',
|
|
7989
|
+
modalities: {
|
|
7990
|
+
input: ['text'],
|
|
7991
|
+
output: ['text'],
|
|
7992
|
+
},
|
|
7993
|
+
toolCall: true,
|
|
7994
|
+
reasoningText: true,
|
|
7995
|
+
attachment: false,
|
|
7996
|
+
temperature: false,
|
|
7997
|
+
knowledge: '2025-08-31',
|
|
7998
|
+
releaseDate: '2026-02-12',
|
|
7999
|
+
lastUpdated: '2026-02-12',
|
|
8000
|
+
openWeights: false,
|
|
8001
|
+
cost: {
|
|
8002
|
+
input: 1.75,
|
|
8003
|
+
output: 14,
|
|
8004
|
+
cacheRead: 0.175,
|
|
8005
|
+
},
|
|
8006
|
+
limit: {
|
|
8007
|
+
context: 128000,
|
|
8008
|
+
output: 128000,
|
|
8009
|
+
},
|
|
8010
|
+
provider: {
|
|
8011
|
+
npm: '@ai-sdk/openai',
|
|
8012
|
+
},
|
|
8013
|
+
},
|
|
8014
|
+
{
|
|
8015
|
+
id: 'gpt-5.4',
|
|
8016
|
+
ownedBy: 'openai',
|
|
8017
|
+
label: 'GPT-5.4',
|
|
8018
|
+
modalities: {
|
|
8019
|
+
input: ['text', 'image', 'pdf'],
|
|
8020
|
+
output: ['text'],
|
|
8021
|
+
},
|
|
8022
|
+
toolCall: true,
|
|
8023
|
+
reasoningText: true,
|
|
8024
|
+
attachment: true,
|
|
8025
|
+
temperature: false,
|
|
8026
|
+
knowledge: '2025-08-31',
|
|
8027
|
+
releaseDate: '2026-03-05',
|
|
8028
|
+
lastUpdated: '2026-03-05',
|
|
8029
|
+
openWeights: false,
|
|
8030
|
+
cost: {
|
|
8031
|
+
input: 2.5,
|
|
8032
|
+
output: 15,
|
|
8033
|
+
cacheRead: 0.25,
|
|
8034
|
+
},
|
|
8035
|
+
limit: {
|
|
8036
|
+
context: 1050000,
|
|
8037
|
+
output: 128000,
|
|
8038
|
+
},
|
|
8039
|
+
provider: {
|
|
8040
|
+
npm: '@ai-sdk/openai',
|
|
8041
|
+
},
|
|
8042
|
+
},
|
|
8043
|
+
{
|
|
8044
|
+
id: 'gpt-5.4-pro',
|
|
8045
|
+
ownedBy: 'openai',
|
|
8046
|
+
label: 'GPT-5.4 Pro',
|
|
8047
|
+
modalities: {
|
|
8048
|
+
input: ['text', 'image', 'pdf'],
|
|
8049
|
+
output: ['text'],
|
|
8050
|
+
},
|
|
8051
|
+
toolCall: true,
|
|
8052
|
+
reasoningText: true,
|
|
8053
|
+
attachment: true,
|
|
8054
|
+
temperature: false,
|
|
8055
|
+
knowledge: '2025-08-31',
|
|
8056
|
+
releaseDate: '2026-03-05',
|
|
8057
|
+
lastUpdated: '2026-03-05',
|
|
8058
|
+
openWeights: false,
|
|
8059
|
+
cost: {
|
|
8060
|
+
input: 30,
|
|
8061
|
+
output: 180,
|
|
8062
|
+
cacheRead: 30,
|
|
8063
|
+
},
|
|
8064
|
+
limit: {
|
|
8065
|
+
context: 1050000,
|
|
8066
|
+
output: 128000,
|
|
8067
|
+
},
|
|
8068
|
+
provider: {
|
|
8069
|
+
npm: '@ai-sdk/openai',
|
|
8070
|
+
},
|
|
8071
|
+
},
|
|
7802
8072
|
{
|
|
7803
8073
|
id: 'grok-code',
|
|
7804
8074
|
ownedBy: 'xai',
|
|
@@ -7900,7 +8170,7 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
|
|
|
7900
8170
|
},
|
|
7901
8171
|
limit: {
|
|
7902
8172
|
context: 262144,
|
|
7903
|
-
output:
|
|
8173
|
+
output: 65536,
|
|
7904
8174
|
},
|
|
7905
8175
|
},
|
|
7906
8176
|
{
|
|
@@ -9145,6 +9415,31 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
|
|
|
9145
9415
|
output: 64000,
|
|
9146
9416
|
},
|
|
9147
9417
|
},
|
|
9418
|
+
{
|
|
9419
|
+
id: 'gemini-3.1-pro-preview',
|
|
9420
|
+
ownedBy: 'google',
|
|
9421
|
+
label: 'Gemini 3.1 Pro Preview',
|
|
9422
|
+
modalities: {
|
|
9423
|
+
input: ['text', 'image'],
|
|
9424
|
+
output: ['text'],
|
|
9425
|
+
},
|
|
9426
|
+
toolCall: true,
|
|
9427
|
+
reasoningText: true,
|
|
9428
|
+
attachment: true,
|
|
9429
|
+
temperature: true,
|
|
9430
|
+
knowledge: '2025-01',
|
|
9431
|
+
releaseDate: '2026-02-19',
|
|
9432
|
+
lastUpdated: '2026-02-19',
|
|
9433
|
+
openWeights: false,
|
|
9434
|
+
cost: {
|
|
9435
|
+
input: 0,
|
|
9436
|
+
output: 0,
|
|
9437
|
+
},
|
|
9438
|
+
limit: {
|
|
9439
|
+
context: 128000,
|
|
9440
|
+
output: 64000,
|
|
9441
|
+
},
|
|
9442
|
+
},
|
|
9148
9443
|
{
|
|
9149
9444
|
id: 'gpt-4.1',
|
|
9150
9445
|
ownedBy: 'openai',
|
|
@@ -9395,6 +9690,56 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
|
|
|
9395
9690
|
output: 128000,
|
|
9396
9691
|
},
|
|
9397
9692
|
},
|
|
9693
|
+
{
|
|
9694
|
+
id: 'gpt-5.3-codex',
|
|
9695
|
+
ownedBy: 'openai',
|
|
9696
|
+
label: 'GPT-5.3-Codex',
|
|
9697
|
+
modalities: {
|
|
9698
|
+
input: ['text', 'image'],
|
|
9699
|
+
output: ['text'],
|
|
9700
|
+
},
|
|
9701
|
+
toolCall: true,
|
|
9702
|
+
reasoningText: true,
|
|
9703
|
+
attachment: false,
|
|
9704
|
+
temperature: false,
|
|
9705
|
+
knowledge: '2025-08-31',
|
|
9706
|
+
releaseDate: '2026-02-24',
|
|
9707
|
+
lastUpdated: '2026-02-24',
|
|
9708
|
+
openWeights: false,
|
|
9709
|
+
cost: {
|
|
9710
|
+
input: 0,
|
|
9711
|
+
output: 0,
|
|
9712
|
+
},
|
|
9713
|
+
limit: {
|
|
9714
|
+
context: 400000,
|
|
9715
|
+
output: 128000,
|
|
9716
|
+
},
|
|
9717
|
+
},
|
|
9718
|
+
{
|
|
9719
|
+
id: 'gpt-5.4',
|
|
9720
|
+
ownedBy: 'openai',
|
|
9721
|
+
label: 'GPT-5.4',
|
|
9722
|
+
modalities: {
|
|
9723
|
+
input: ['text', 'image'],
|
|
9724
|
+
output: ['text'],
|
|
9725
|
+
},
|
|
9726
|
+
toolCall: true,
|
|
9727
|
+
reasoningText: true,
|
|
9728
|
+
attachment: false,
|
|
9729
|
+
temperature: false,
|
|
9730
|
+
knowledge: '2025-08-31',
|
|
9731
|
+
releaseDate: '2026-03-05',
|
|
9732
|
+
lastUpdated: '2026-03-05',
|
|
9733
|
+
openWeights: false,
|
|
9734
|
+
cost: {
|
|
9735
|
+
input: 0,
|
|
9736
|
+
output: 0,
|
|
9737
|
+
},
|
|
9738
|
+
limit: {
|
|
9739
|
+
context: 400000,
|
|
9740
|
+
output: 128000,
|
|
9741
|
+
},
|
|
9742
|
+
},
|
|
9398
9743
|
{
|
|
9399
9744
|
id: 'grok-code-fast-1',
|
|
9400
9745
|
ownedBy: 'xai',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createOpenAI } from '@ai-sdk/openai';
|
|
2
2
|
import type { OAuth } from '../../types/src/index.ts';
|
|
3
3
|
|
|
4
4
|
const COPILOT_BASE_URL = 'https://api.githubcopilot.com';
|
|
@@ -7,6 +7,149 @@ export type CopilotOAuthConfig = {
|
|
|
7
7
|
oauth: OAuth;
|
|
8
8
|
};
|
|
9
9
|
|
|
10
|
+
const COPILOT_REASONING_DROP_TYPES = new Set([
|
|
11
|
+
'response.reasoning.delta',
|
|
12
|
+
'response.reasoning.done',
|
|
13
|
+
'response.reasoning_summary_part.added',
|
|
14
|
+
'response.reasoning_summary_text.delta',
|
|
15
|
+
'response.reasoning_summary_part.done',
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
function shouldDropCopilotEvent(data: string): boolean {
|
|
19
|
+
try {
|
|
20
|
+
const parsed = JSON.parse(data) as Record<string, unknown>;
|
|
21
|
+
const type = typeof parsed.type === 'string' ? parsed.type : '';
|
|
22
|
+
if (!type) return false;
|
|
23
|
+
|
|
24
|
+
if (COPILOT_REASONING_DROP_TYPES.has(type)) return true;
|
|
25
|
+
|
|
26
|
+
if (
|
|
27
|
+
(type === 'response.output_item.added' ||
|
|
28
|
+
type === 'response.output_item.done') &&
|
|
29
|
+
parsed.item &&
|
|
30
|
+
typeof parsed.item === 'object'
|
|
31
|
+
) {
|
|
32
|
+
return (parsed.item as Record<string, unknown>).type === 'reasoning';
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return false;
|
|
36
|
+
} catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function filterSseEvent(rawEvent: string): string | null {
|
|
42
|
+
if (!rawEvent.trim()) return rawEvent;
|
|
43
|
+
|
|
44
|
+
const dataLines: string[] = [];
|
|
45
|
+
for (const line of rawEvent.split('\n')) {
|
|
46
|
+
if (line.startsWith('data:')) {
|
|
47
|
+
dataLines.push(line.slice('data:'.length).trimStart());
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (!dataLines.length) return rawEvent;
|
|
52
|
+
|
|
53
|
+
const data = dataLines.join('\n');
|
|
54
|
+
if (data === '[DONE]') return rawEvent;
|
|
55
|
+
|
|
56
|
+
if (shouldDropCopilotEvent(data)) return null;
|
|
57
|
+
return rawEvent;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const SYNTHETIC_COMPLETED =
|
|
61
|
+
'data: {"type":"response.completed","response":{"status":"completed","incomplete_details":null,"usage":{"input_tokens":0,"output_tokens":0}}}';
|
|
62
|
+
|
|
63
|
+
function sanitizeCopilotResponsesStream(response: Response): Response {
|
|
64
|
+
if (!response.body) return response;
|
|
65
|
+
|
|
66
|
+
const decoder = new TextDecoder();
|
|
67
|
+
const encoder = new TextEncoder();
|
|
68
|
+
let buffer = '';
|
|
69
|
+
let seenCompleted = false;
|
|
70
|
+
let seenDone = false;
|
|
71
|
+
|
|
72
|
+
function processBuffer(
|
|
73
|
+
controller: TransformStreamDefaultController<Uint8Array>,
|
|
74
|
+
) {
|
|
75
|
+
let boundary = buffer.indexOf('\n\n');
|
|
76
|
+
while (boundary !== -1) {
|
|
77
|
+
const rawEvent = buffer.slice(0, boundary);
|
|
78
|
+
buffer = buffer.slice(boundary + 2);
|
|
79
|
+
|
|
80
|
+
const filtered = filterSseEvent(rawEvent);
|
|
81
|
+
if (filtered !== null) {
|
|
82
|
+
const dataLine = filtered
|
|
83
|
+
.split('\n')
|
|
84
|
+
.find((l) => l.startsWith('data:'));
|
|
85
|
+
const d = dataLine?.slice(5).trim();
|
|
86
|
+
if (d === '[DONE]') {
|
|
87
|
+
seenDone = true;
|
|
88
|
+
} else if (d) {
|
|
89
|
+
try {
|
|
90
|
+
const p = JSON.parse(d) as Record<string, unknown>;
|
|
91
|
+
if (
|
|
92
|
+
p.type === 'response.completed' ||
|
|
93
|
+
p.type === 'response.incomplete'
|
|
94
|
+
) {
|
|
95
|
+
seenCompleted = true;
|
|
96
|
+
}
|
|
97
|
+
} catch {}
|
|
98
|
+
}
|
|
99
|
+
controller.enqueue(encoder.encode(`${filtered}\n\n`));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
boundary = buffer.indexOf('\n\n');
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const transform = new TransformStream<Uint8Array, Uint8Array>({
|
|
107
|
+
transform(chunk, controller) {
|
|
108
|
+
buffer += decoder.decode(chunk, { stream: true }).replace(/\r\n/g, '\n');
|
|
109
|
+
processBuffer(controller);
|
|
110
|
+
},
|
|
111
|
+
flush(controller) {
|
|
112
|
+
buffer += decoder.decode().replace(/\r\n/g, '\n');
|
|
113
|
+
if (buffer.trim()) {
|
|
114
|
+
buffer += '\n\n';
|
|
115
|
+
processBuffer(controller);
|
|
116
|
+
}
|
|
117
|
+
if (!seenCompleted) {
|
|
118
|
+
controller.enqueue(encoder.encode(`${SYNTHETIC_COMPLETED}\n\n`));
|
|
119
|
+
}
|
|
120
|
+
if (!seenDone) {
|
|
121
|
+
controller.enqueue(encoder.encode('data: [DONE]\n\n'));
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
return new Response(response.body.pipeThrough(transform), {
|
|
127
|
+
status: response.status,
|
|
128
|
+
statusText: response.statusText,
|
|
129
|
+
headers: response.headers,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function sanitizeCopilotRequestBody(body: string): string {
|
|
134
|
+
try {
|
|
135
|
+
const parsed = JSON.parse(body);
|
|
136
|
+
delete parsed.store;
|
|
137
|
+
delete parsed.previous_response_id;
|
|
138
|
+
if (Array.isArray(parsed.input)) {
|
|
139
|
+
for (const item of parsed.input) {
|
|
140
|
+
if (item && typeof item === 'object') {
|
|
141
|
+
if (item.type === 'function_call' && 'id' in item) {
|
|
142
|
+
delete item.id;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return JSON.stringify(parsed);
|
|
148
|
+
} catch {
|
|
149
|
+
return body;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
10
153
|
export function createCopilotFetch(config: CopilotOAuthConfig): typeof fetch {
|
|
11
154
|
return async (
|
|
12
155
|
input: string | URL | Request,
|
|
@@ -19,21 +162,50 @@ export function createCopilotFetch(config: CopilotOAuthConfig): typeof fetch {
|
|
|
19
162
|
headers.set('Openai-Intent', 'conversation-edits');
|
|
20
163
|
headers.set('User-Agent', 'ottocode');
|
|
21
164
|
|
|
22
|
-
|
|
165
|
+
const requestUrl =
|
|
166
|
+
typeof input === 'string'
|
|
167
|
+
? input
|
|
168
|
+
: input instanceof URL
|
|
169
|
+
? input.href
|
|
170
|
+
: input.url;
|
|
171
|
+
|
|
172
|
+
if (requestUrl.includes('/responses') && typeof init?.body === 'string') {
|
|
173
|
+
init = { ...init, body: sanitizeCopilotRequestBody(init.body) };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const response = await fetch(input, {
|
|
23
177
|
...init,
|
|
24
178
|
headers,
|
|
25
179
|
});
|
|
180
|
+
|
|
181
|
+
if (requestUrl.includes('/responses') && response.ok) {
|
|
182
|
+
return sanitizeCopilotResponsesStream(response);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return response;
|
|
26
186
|
};
|
|
27
187
|
}
|
|
28
188
|
|
|
189
|
+
function isGpt5OrLater(model: string): boolean {
|
|
190
|
+
const match = /^gpt-(\d+)/.exec(model);
|
|
191
|
+
if (!match) return false;
|
|
192
|
+
return Number(match[1]) >= 5;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function needsResponsesApi(model: string): boolean {
|
|
196
|
+
return isGpt5OrLater(model) && !model.startsWith('gpt-5-mini');
|
|
197
|
+
}
|
|
198
|
+
|
|
29
199
|
export function createCopilotModel(model: string, config: CopilotOAuthConfig) {
|
|
30
200
|
const customFetch = createCopilotFetch(config);
|
|
31
201
|
|
|
32
|
-
const provider =
|
|
33
|
-
name: 'github-copilot',
|
|
34
|
-
baseURL: COPILOT_BASE_URL,
|
|
202
|
+
const provider = createOpenAI({
|
|
35
203
|
apiKey: 'copilot-oauth',
|
|
204
|
+
baseURL: COPILOT_BASE_URL,
|
|
36
205
|
fetch: customFetch,
|
|
37
206
|
});
|
|
38
|
-
|
|
207
|
+
|
|
208
|
+
return needsResponsesApi(model)
|
|
209
|
+
? provider.responses(model)
|
|
210
|
+
: provider.chat(model);
|
|
39
211
|
}
|
package/src/providers/src/env.ts
CHANGED
|
@@ -19,6 +19,14 @@ export function providerEnvVar(provider: ProviderId): string {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export function readEnvKey(provider: ProviderId): string | undefined {
|
|
22
|
+
if (provider === 'copilot') {
|
|
23
|
+
const copilotToken =
|
|
24
|
+
process.env.COPILOT_GITHUB_TOKEN ??
|
|
25
|
+
process.env.GH_TOKEN ??
|
|
26
|
+
process.env.GITHUB_TOKEN;
|
|
27
|
+
return copilotToken?.length ? copilotToken : undefined;
|
|
28
|
+
}
|
|
29
|
+
|
|
22
30
|
const key = providerEnvVar(provider);
|
|
23
31
|
const value = process.env[key];
|
|
24
32
|
return value?.length ? value : undefined;
|
|
@@ -8,22 +8,35 @@ const OAUTH_MODEL_PREFIXES: Partial<Record<ProviderId, string[]>> = {
|
|
|
8
8
|
'claude-sonnet-4-5',
|
|
9
9
|
'claude-sonnet-4-6',
|
|
10
10
|
],
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const OAUTH_MODEL_IDS: Partial<Record<ProviderId, string[]>> = {
|
|
11
14
|
openai: [
|
|
12
|
-
'gpt-5.
|
|
13
|
-
'gpt-5.3-codex',
|
|
15
|
+
'gpt-5.1-codex',
|
|
14
16
|
'gpt-5.1-codex-max',
|
|
15
17
|
'gpt-5.1-codex-mini',
|
|
16
18
|
'gpt-5.2',
|
|
19
|
+
'gpt-5.2-codex',
|
|
20
|
+
'gpt-5.3-codex',
|
|
21
|
+
'gpt-5.4',
|
|
17
22
|
],
|
|
18
23
|
};
|
|
19
24
|
|
|
25
|
+
function matchesOAuthModel(provider: ProviderId, modelId: string): boolean {
|
|
26
|
+
const exactIds = OAUTH_MODEL_IDS[provider];
|
|
27
|
+
if (exactIds?.includes(modelId)) return true;
|
|
28
|
+
|
|
29
|
+
const prefixes = OAUTH_MODEL_PREFIXES[provider];
|
|
30
|
+
if (prefixes?.some((prefix) => modelId.startsWith(prefix))) return true;
|
|
31
|
+
|
|
32
|
+
return !exactIds && !prefixes;
|
|
33
|
+
}
|
|
34
|
+
|
|
20
35
|
export function isModelAllowedForOAuth(
|
|
21
36
|
provider: ProviderId,
|
|
22
37
|
modelId: string,
|
|
23
38
|
): boolean {
|
|
24
|
-
|
|
25
|
-
if (!prefixes) return true;
|
|
26
|
-
return prefixes.some((prefix) => modelId.startsWith(prefix));
|
|
39
|
+
return matchesOAuthModel(provider, modelId);
|
|
27
40
|
}
|
|
28
41
|
|
|
29
42
|
export function filterModelsForAuthType(
|
|
@@ -32,15 +45,14 @@ export function filterModelsForAuthType(
|
|
|
32
45
|
authType: 'api' | 'oauth' | 'wallet' | undefined,
|
|
33
46
|
): ModelInfo[] {
|
|
34
47
|
if (authType !== 'oauth') return models;
|
|
48
|
+
const exactIds = OAUTH_MODEL_IDS[provider];
|
|
35
49
|
const prefixes = OAUTH_MODEL_PREFIXES[provider];
|
|
36
|
-
if (!prefixes) return models;
|
|
37
|
-
return models.filter((
|
|
38
|
-
prefixes.some((prefix) => m.id.startsWith(prefix)),
|
|
39
|
-
);
|
|
50
|
+
if (!exactIds && !prefixes) return models;
|
|
51
|
+
return models.filter((model) => matchesOAuthModel(provider, model.id));
|
|
40
52
|
}
|
|
41
53
|
|
|
42
54
|
export function getOAuthModelPrefixes(
|
|
43
55
|
provider: ProviderId,
|
|
44
56
|
): string[] | undefined {
|
|
45
|
-
return OAUTH_MODEL_PREFIXES[provider];
|
|
57
|
+
return OAUTH_MODEL_PREFIXES[provider] ?? OAUTH_MODEL_IDS[provider];
|
|
46
58
|
}
|
|
@@ -39,6 +39,11 @@ const PREFERRED_FAST_MODELS: Partial<Record<ProviderId, string[]>> = {
|
|
|
39
39
|
copilot: ['gpt-4.1-mini'],
|
|
40
40
|
};
|
|
41
41
|
|
|
42
|
+
const PREFERRED_FAST_MODELS_OAUTH: Partial<Record<ProviderId, string[]>> = {
|
|
43
|
+
openai: ['gpt-5.1-codex-mini'],
|
|
44
|
+
anthropic: ['claude-haiku-4-5'],
|
|
45
|
+
};
|
|
46
|
+
|
|
42
47
|
export function getFastModel(provider: ProviderId): string | undefined {
|
|
43
48
|
const providerModels = catalog[provider]?.models ?? [];
|
|
44
49
|
if (!providerModels.length) return undefined;
|
|
@@ -71,12 +76,12 @@ export function getFastModelForAuth(
|
|
|
71
76
|
);
|
|
72
77
|
if (!filteredModels.length) return getFastModel(provider);
|
|
73
78
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
79
|
+
const preferredMap =
|
|
80
|
+
authType === 'oauth' ? PREFERRED_FAST_MODELS_OAUTH : PREFERRED_FAST_MODELS;
|
|
81
|
+
const preferred = preferredMap[provider] ?? [];
|
|
82
|
+
for (const modelId of preferred) {
|
|
83
|
+
if (filteredModels.some((m) => m.id === modelId)) {
|
|
84
|
+
return modelId;
|
|
80
85
|
}
|
|
81
86
|
}
|
|
82
87
|
|