Skip to content

Commit 0bc9bf0

Browse files
committed
feat: Anthropic Cost
1 parent e38d789 commit 0bc9bf0

File tree

7 files changed

+344
-80
lines changed

7 files changed

+344
-80
lines changed

OpenAIAPIUsage.xcodeproj/.xcodesamplecode.plist

Whitespace-only changes.

OpenAIAPIUsage.xcodeproj/project.pbxproj

Lines changed: 11 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,13 @@
33
archiveVersion = 1;
44
classes = {
55
};
6-
objectVersion = 56;
6+
objectVersion = 70;
77
objects = {
88

99
/* Begin PBXBuildFile section */
10-
EC16FD072B8497A300BDA8BC /* OpenAIAPIUsageApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC16FD062B8497A300BDA8BC /* OpenAIAPIUsageApp.swift */; };
11-
EC16FD0D2B8497A400BDA8BC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = EC16FD0C2B8497A400BDA8BC /* Assets.xcassets */; };
12-
EC16FD102B8497A400BDA8BC /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = EC16FD0F2B8497A400BDA8BC /* Preview Assets.xcassets */; };
1310
EC16FD1B2B8497A500BDA8BC /* OpenAIAPIUsageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC16FD1A2B8497A500BDA8BC /* OpenAIAPIUsageTests.swift */; };
1411
EC16FD252B8497A500BDA8BC /* OpenAIAPIUsageUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC16FD242B8497A500BDA8BC /* OpenAIAPIUsageUITests.swift */; };
1512
EC16FD272B8497A500BDA8BC /* OpenAIAPIUsageUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC16FD262B8497A500BDA8BC /* OpenAIAPIUsageUITestsLaunchTests.swift */; };
16-
EC16FD342B8499B600BDA8BC /* Usage.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC16FD332B8499B600BDA8BC /* Usage.swift */; };
17-
EC16FD362B8499D400BDA8BC /* WatchDog.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC16FD352B8499D400BDA8BC /* WatchDog.swift */; };
18-
EC16FD382B849A0700BDA8BC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC16FD372B849A0700BDA8BC /* AppDelegate.swift */; };
19-
EC16FD3A2B849A8900BDA8BC /* API.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC16FD392B849A8900BDA8BC /* API.swift */; };
2013
/* End PBXBuildFile section */
2114

2215
/* Begin PBXContainerItemProxy section */
@@ -38,21 +31,18 @@
3831

3932
/* Begin PBXFileReference section */
4033
EC16FD032B8497A300BDA8BC /* OpenAIAPIUsage.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = OpenAIAPIUsage.app; sourceTree = BUILT_PRODUCTS_DIR; };
41-
EC16FD062B8497A300BDA8BC /* OpenAIAPIUsageApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenAIAPIUsageApp.swift; sourceTree = "<group>"; };
42-
EC16FD0C2B8497A400BDA8BC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
43-
EC16FD0F2B8497A400BDA8BC /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
44-
EC16FD112B8497A400BDA8BC /* OpenAIAPIUsage.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = OpenAIAPIUsage.entitlements; sourceTree = "<group>"; };
4534
EC16FD162B8497A400BDA8BC /* OpenAIAPIUsageTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = OpenAIAPIUsageTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
4635
EC16FD1A2B8497A500BDA8BC /* OpenAIAPIUsageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenAIAPIUsageTests.swift; sourceTree = "<group>"; };
4736
EC16FD202B8497A500BDA8BC /* OpenAIAPIUsageUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = OpenAIAPIUsageUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
4837
EC16FD242B8497A500BDA8BC /* OpenAIAPIUsageUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenAIAPIUsageUITests.swift; sourceTree = "<group>"; };
4938
EC16FD262B8497A500BDA8BC /* OpenAIAPIUsageUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenAIAPIUsageUITestsLaunchTests.swift; sourceTree = "<group>"; };
50-
EC16FD332B8499B600BDA8BC /* Usage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Usage.swift; sourceTree = "<group>"; };
51-
EC16FD352B8499D400BDA8BC /* WatchDog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchDog.swift; sourceTree = "<group>"; };
52-
EC16FD372B849A0700BDA8BC /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
53-
EC16FD392B849A8900BDA8BC /* API.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = API.swift; sourceTree = "<group>"; };
39+
EC8703702D82155100915CE5 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
5440
/* End PBXFileReference section */
5541

42+
/* Begin PBXFileSystemSynchronizedRootGroup section */
43+
EC8703652D81FF6E00915CE5 /* OpenAIAPIUsage */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = OpenAIAPIUsage; sourceTree = "<group>"; };
44+
/* End PBXFileSystemSynchronizedRootGroup section */
45+
5646
/* Begin PBXFrameworksBuildPhase section */
5747
EC16FD002B8497A300BDA8BC /* Frameworks */ = {
5848
isa = PBXFrameworksBuildPhase;
@@ -81,9 +71,10 @@
8171
EC16FCFA2B8497A300BDA8BC = {
8272
isa = PBXGroup;
8373
children = (
84-
EC16FD052B8497A300BDA8BC /* OpenAIAPIUsage */,
74+
EC8703652D81FF6E00915CE5 /* OpenAIAPIUsage */,
8575
EC16FD192B8497A500BDA8BC /* OpenAIAPIUsageTests */,
8676
EC16FD232B8497A500BDA8BC /* OpenAIAPIUsageUITests */,
77+
EC8703702D82155100915CE5 /* README.md */,
8778
EC16FD042B8497A300BDA8BC /* Products */,
8879
);
8980
sourceTree = "<group>";
@@ -98,29 +89,6 @@
9889
name = Products;
9990
sourceTree = "<group>";
10091
};
101-
EC16FD052B8497A300BDA8BC /* OpenAIAPIUsage */ = {
102-
isa = PBXGroup;
103-
children = (
104-
EC16FD062B8497A300BDA8BC /* OpenAIAPIUsageApp.swift */,
105-
EC16FD372B849A0700BDA8BC /* AppDelegate.swift */,
106-
EC16FD352B8499D400BDA8BC /* WatchDog.swift */,
107-
EC16FD392B849A8900BDA8BC /* API.swift */,
108-
EC16FD332B8499B600BDA8BC /* Usage.swift */,
109-
EC16FD0C2B8497A400BDA8BC /* Assets.xcassets */,
110-
EC16FD112B8497A400BDA8BC /* OpenAIAPIUsage.entitlements */,
111-
EC16FD0E2B8497A400BDA8BC /* Preview Content */,
112-
);
113-
path = OpenAIAPIUsage;
114-
sourceTree = "<group>";
115-
};
116-
EC16FD0E2B8497A400BDA8BC /* Preview Content */ = {
117-
isa = PBXGroup;
118-
children = (
119-
EC16FD0F2B8497A400BDA8BC /* Preview Assets.xcassets */,
120-
);
121-
path = "Preview Content";
122-
sourceTree = "<group>";
123-
};
12492
EC16FD192B8497A500BDA8BC /* OpenAIAPIUsageTests */ = {
12593
isa = PBXGroup;
12694
children = (
@@ -153,6 +121,9 @@
153121
);
154122
dependencies = (
155123
);
124+
fileSystemSynchronizedGroups = (
125+
EC8703652D81FF6E00915CE5 /* OpenAIAPIUsage */,
126+
);
156127
name = OpenAIAPIUsage;
157128
productName = OpenAIAPIUsage;
158129
productReference = EC16FD032B8497A300BDA8BC /* OpenAIAPIUsage.app */;
@@ -242,8 +213,6 @@
242213
isa = PBXResourcesBuildPhase;
243214
buildActionMask = 2147483647;
244215
files = (
245-
EC16FD102B8497A400BDA8BC /* Preview Assets.xcassets in Resources */,
246-
EC16FD0D2B8497A400BDA8BC /* Assets.xcassets in Resources */,
247216
);
248217
runOnlyForDeploymentPostprocessing = 0;
249218
};
@@ -268,11 +237,6 @@
268237
isa = PBXSourcesBuildPhase;
269238
buildActionMask = 2147483647;
270239
files = (
271-
EC16FD382B849A0700BDA8BC /* AppDelegate.swift in Sources */,
272-
EC16FD072B8497A300BDA8BC /* OpenAIAPIUsageApp.swift in Sources */,
273-
EC16FD342B8499B600BDA8BC /* Usage.swift in Sources */,
274-
EC16FD362B8499D400BDA8BC /* WatchDog.swift in Sources */,
275-
EC16FD3A2B849A8900BDA8BC /* API.swift in Sources */,
276240
);
277241
runOnlyForDeploymentPostprocessing = 0;
278242
};
@@ -498,7 +462,6 @@
498462
EC16FD2E2B8497A500BDA8BC /* Debug */ = {
499463
isa = XCBuildConfiguration;
500464
buildSettings = {
501-
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
502465
BUNDLE_LOADER = "$(TEST_HOST)";
503466
CODE_SIGN_STYLE = Automatic;
504467
CURRENT_PROJECT_VERSION = 1;
@@ -517,7 +480,6 @@
517480
EC16FD2F2B8497A500BDA8BC /* Release */ = {
518481
isa = XCBuildConfiguration;
519482
buildSettings = {
520-
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
521483
BUNDLE_LOADER = "$(TEST_HOST)";
522484
CODE_SIGN_STYLE = Automatic;
523485
CURRENT_PROJECT_VERSION = 1;
@@ -536,7 +498,6 @@
536498
EC16FD312B8497A500BDA8BC /* Debug */ = {
537499
isa = XCBuildConfiguration;
538500
buildSettings = {
539-
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
540501
CODE_SIGN_STYLE = Automatic;
541502
CURRENT_PROJECT_VERSION = 1;
542503
DEVELOPMENT_TEAM = NA5BE2D52P;
@@ -553,7 +514,6 @@
553514
EC16FD322B8497A500BDA8BC /* Release */ = {
554515
isa = XCBuildConfiguration;
555516
buildSettings = {
556-
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
557517
CODE_SIGN_STYLE = Automatic;
558518
CURRENT_PROJECT_VERSION = 1;
559519
DEVELOPMENT_TEAM = NA5BE2D52P;

OpenAIAPIUsage/API.swift

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import Foundation
99

1010
struct API {
11-
static func getUsage(_ bearerToken: String) async -> Double? {
11+
static func getOpenAIUsage(_ bearerToken: String) async -> Double? {
1212
let (startDate, endDate) = getFirstDaysOfCurrentAndNextMonth() //getTodaysAndNextMonthsFirstDate()
1313
print(startDate, endDate)
1414
let urlString = "https://api.openai.com/dashboard/billing/usage?end_date=\(endDate)&start_date=\(startDate)"
@@ -30,6 +30,32 @@ struct API {
3030
}
3131
}
3232

33+
static func getAnthropicUsage(cookie: String, organizationID: String) async -> Double? {
34+
let (startDate, endDate) = getFirstDaysOfCurrentAndNextMonth() //getTodaysAndNextMonthsFirstDate()
35+
print(startDate, endDate)
36+
let urlString = "https://console.anthropic.com/api/organizations/\(organizationID)/usage_cost?starting_on=\(startDate)&ending_before=\(endDate)&group_by=workspace_id"
37+
guard let url = URL(string: urlString) else {
38+
print("Invalid urlString = \(urlString)")
39+
return nil
40+
}
41+
var request = URLRequest(url: url)
42+
request.httpMethod = "GET"
43+
request.addValue(cookie, forHTTPHeaderField: "Cookie")
44+
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
45+
request.addValue("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3", forHTTPHeaderField: "User-Agent")
46+
47+
do {
48+
let (data, _) = try await URLSession.shared.data(for: request)
49+
let string = String(data: data, encoding: .utf8)
50+
let decoder = JSONDecoder()
51+
let usage = try decoder.decode(CostData.self, from: data)
52+
return usage.totalCost
53+
} catch {
54+
print(error.localizedDescription)
55+
return nil
56+
}
57+
}
58+
3359
private static func getFirstDaysOfCurrentAndNextMonth() -> (String, String) {
3460
let dateFormatter = DateFormatter()
3561
dateFormatter.dateFormat = "yyyy-MM-dd"
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//
2+
// CostData.swift
3+
// OpenAIAPIUsage
4+
//
5+
// Created by Liang on 12-03-2025.
6+
//
7+
8+
9+
struct CostData: Codable {
10+
let costs: [String: [CostEntry]]
11+
12+
enum CodingKeys: String, CodingKey {
13+
case costs = "costs"
14+
}
15+
// Computed property to sum up all the totals.
16+
var totalCost: Double {
17+
let cost = costs.values.flatMap { $0 }.reduce(0.0) { $0 + $1.total }
18+
return cost.rounded(.down)
19+
}
20+
}
21+
22+
struct CostEntry: Codable {
23+
let workspaceId: String
24+
let modelName: String
25+
let total: Double
26+
let tokenType: String
27+
let promptTokenCountTier: String
28+
let usageType: String
29+
30+
enum CodingKeys: String, CodingKey {
31+
case workspaceId = "workspace_id"
32+
case modelName = "model_name"
33+
case total
34+
case tokenType = "token_type"
35+
case promptTokenCountTier = "prompt_token_count_tier"
36+
case usageType = "usage_type"
37+
}
38+
}

0 commit comments

Comments
 (0)