Skip to content

Commit

Permalink
Merge pull request #68 from fumito-ito/feature/admin-api
Browse files Browse the repository at this point in the history
Admin API support
  • Loading branch information
fumito-ito authored Dec 12, 2024
2 parents dcfedf3 + b269538 commit 30e7e34
Show file tree
Hide file tree
Showing 85 changed files with 2,606 additions and 171 deletions.
185 changes: 131 additions & 54 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,137 @@ for try await chunk in stream {
}
```

### [Message Batches (beta)](https://docs.anthropic.com/en/docs/build-with-claude/message-batches)

The Message Batches API is a powerful, cost-effective way to asynchronously process large volumes of [Messages](https://docs.anthropic.com/en/api/messages) requests. This approach is well-suited to tasks that do not require immediate responses, reducing costs by 50% while increasing throughput.

This is especially useful for bulk operations that don’t require immediate results.

Here's an example of how to process many messages with the Message Batches API:

```swift
let anthropic = Anthropic(apiKey: "YOUR_OWN_API_KEY")

let messages = [
Message(role: .user, content: [.text("Write a haiku about robots.")]),
Message(role: .user, content: [.text("Write a haiku about robots. Skip the preamble; go straight into the poem.")]),
Message(role: .user, content: [.text("Who is the best basketball player of all time?")]),
Message(role: .user, content: [.text("Who is the best basketball player of all time? Yes, there are differing opinions, but if you absolutely had to pick one player, who would it be?")])
// ....
]

let batch = MessageBatch(
customId: "my-first-batch-request",
parameter: .init(
messages: messages,
maxTokens: 1024
)
)

let response = try await anthropic.messageBatches.createBatches(batches: [batch])
```

### [Admin API](https://docs.anthropic.com/en/docs/administration/administration-api)

This library also supports an Admin API for managing workspaces and organization members.

#### [Organization Members API](https://docs.anthropic.com/en/docs/administration/administration-api#organization-members)

- **Get Organization Member**: Retrieve details about a specific organization member.
- **List Organization Members**: List organization members.
- **Remove Organization Member**: Remove a member from the organization.
- **Update Organization Member**: Update the role or details of an existing organization member.

Example of updating an organization member:

```swift
let admin = AnthropicAdmin(adminAPIKey: "YOUR_OWN_ADMIN_API_KEY")
try await admin.organizationMembers.get(userId: "user_01WCz1FkmYMm4gnmykNKUu3Q")
```

#### [Organization Invites API](https://docs.anthropic.com/en/docs/administration/administration-api#organization-invites)

- **Get Organization Invites**: Retrieve details about a specific organization invitation.
- **List Organization Invites**: List organization invitations.
- **Remove Organization Invites**: Remove a invitation from the organization.
- **Create Organization Invites**: Create a new organization invitation.

Example of updating an organization invitation:

```swift
let admin = AnthropicAdmin(adminAPIKey: "YOUR_OWN_ADMIN_API_KEY")
try await admin.organizationInvites.list()
```

#### [Workspaces API](https://docs.anthropic.com/en/docs/administration/administration-api#workspaces)

- **Get Workspace**: Retrieve details about a specific workspace.
- **List Workspaces**: List workspaces.
- **Create Workspace**: Create a new workspace.
- **Archive Workspace**: Archive an existing workspace.
- **Update Workspace**: Update the details of an existing workspace.

Example of creating a workspace:

```swift
let admin = AnthropicAdmin(adminAPIKey: "YOUR_OWN_ADMIN_API_KEY")
try await admin.workspaces.get(workspaceId: "wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ")
```

#### [Workspace Members API](https://docs.anthropic.com/en/docs/administration/administration-api#workspace-members)

- **Get Workspace Member**: Retrieve details about a specific workspace member.
- **List Workspace Members**: List workspace members.
- **Create Workspace Member**: Add a new member for the workspace.
- **Remove Workspace Member**: Remove a member from the workspace.
- **Update Workspace Member**: Update the role or details of an existing workspace member.

Example of updating an workspace member:

```swift
let admin = AnthropicAdmin(adminAPIKey: "YOUR_OWN_ADMIN_API_KEY")
try await admin.workspaceMembers.get(userId: "user_01WCz1FkmYMm4gnmykNKUu3Q")
```

#### [API Keys API](https://docs.anthropic.com/en/docs/administration/administration-api#api-keys)

- **Get Organization Member**: Retrieve details about a specific API key.
- **List Organization Members**: List API keys.
- **Update Organization Member**: Update the status or name of an existing API key.

Example of updating an organization member:

```swift
let admin = AnthropicAdmin(adminAPIKey: "YOUR_OWN_ADMIN_API_KEY")
try await admin.apiKeys.get(apiKeyId: "apikey_01Rj2N8SVvo6BePZj99NhmiT")
```

### [Token Counting](https://docs.anthropic.com/en/docs/build-with-claude/token-counting)

Token counting enables you to determine the number of tokens in a message before sending it to Claude, helping you make informed decisions about your prompts and usage. With token counting, you can

- Proactively manage rate limits and costs
- Make smart model routing decisions
- Optimize prompts to be a specific length

```swift
let anthropic = Anthropic(apiKey: "YOUR_OWN_API_KEY")

let message = Message(role: .user, content: [.text("Find flights from San Francisco to a place with warmer weather.")])
let response = try await anthropic.countTokens.countTokens(
[message],
maxTokens: 1024,
tools: [
.computer(.init(name: "my_computer", displayWidthPx: 1024, displayHeightPx: 768, displayNumber: 1),
.bash(.init(name: "bash"))
]
)
```

The token counting endpoint accepts the same structured list of inputs for creating a message, including support for system prompts, tools, images, and PDFs. The response contains the total number of input tokens.

## Supporting Features

### [Tool Use](https://docs.anthropic.com/en/docs/build-with-claude/tool-use)

Claude is capable of interacting with external client-side tools and functions, allowing you to equip Claude with your own custom tools to perform a wider variety of tasks.
Expand Down Expand Up @@ -134,36 +265,6 @@ let response = try await anthropic.messages.createMessage(
)
```

### [Message Batches (beta)](https://docs.anthropic.com/en/docs/build-with-claude/message-batches)

The Message Batches API is a powerful, cost-effective way to asynchronously process large volumes of [Messages](https://docs.anthropic.com/en/api/messages) requests. This approach is well-suited to tasks that do not require immediate responses, reducing costs by 50% while increasing throughput.

This is especially useful for bulk operations that don’t require immediate results.

Here's an example of how to process many messages with the Message Bathches API:

```swift
let anthropic = Anthropic(apiKey: "YOUR_OWN_API_KEY")

let messages = [
Message(role: .user, content: [.text("Write a haiku about robots.")]),
Message(role: .user, content: [.text("Write a haiku about robots. Skip the preamble; go straight into the poem.")]),
Message(role: .user, content: [.text("Who is the best basketball player of all time?")]),
Message(role: .user, content: [.text("Who is the best basketball player of all time? Yes, there are differing opinions, but if you absolutely had to pick one player, who would it be?")])
// ....
]

let batch = MessageBatch(
customId: "my-first-batch-request",
parameter: .init(
messages: messages,
maxTokens: 1024
)
)

let response = try await anthropic.messageBatches.createBatches(batches: [batch])
```

### [Computer Use (beta)](https://docs.anthropic.com/en/docs/build-with-claude/computer-use#computer-tool)

The upgraded Claude 3.5 Sonnet model is capable of interacting with tools that can manipulate a computer desktop environment.
Expand All @@ -184,30 +285,6 @@ let response = try await anthropic.messages.createMessage(
)
```

### [Token Counting](https://docs.anthropic.com/en/docs/build-with-claude/token-counting)

Token counting enables you to determine the number of tokens in a message before sending it to Claude, helping you make informed decisions about your prompts and usage. With token counting, you can

- Proactively manage rate limits and costs
- Make smart model routing decisions
- Optimize prompts to be a specific length

```swift
let anthropic = Anthropic(apiKey: "YOUR_OWN_API_KEY")

let message = Message(role: .user, content: [.text("Find flights from San Francisco to a place with warmer weather.")])
let response = try await anthropic.countTokens.countTokens(
[message],
maxTokens: 1024,
tools: [
.computer(.init(name: "my_computer", displayWidthPx: 1024, displayHeightPx: 768, displayNumber: 1),
.bash(.init(name: "bash"))
]
)
```

The token counting endpoint accepts the same structured list of inputs for creating a message, including support for system prompts, tools, images, and PDFs. The response contains the total number of input tokens.

## Extensions

By introducing an extension Swift package, it is possible to access the Anthropic Claude API through AWS Bedrock and Vertex AI. The supported services are as follows:
Expand Down
1 change: 1 addition & 0 deletions Sources/AnthropicSwiftSDK-TestUtils/HTTPMock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public class HTTPMock: URLProtocol {
client?.urlProtocol(self, didLoad: getBasicJSONStringData())
case .request(_, nil), .requestHeader(_, nil):
client?.urlProtocol(self, didReceive: succeedResponse, cacheStoragePolicy: .notAllowed)
client?.urlProtocol(self, didLoad: getBasicJSONStringData())
case .request(_, let jsonString), .requestHeader(_, let jsonString):
client?.urlProtocol(self, didReceive: succeedResponse, cacheStoragePolicy: .notAllowed)
guard let jsonString, let data = jsonString.data(using: .utf8) else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import AnthropicSwiftSDK
public struct NopRequest: Request {
public let method: HttpMethod
public let path: String
public let queries: [String : any CustomStringConvertible]? = nil
public let queries: [String: any CustomStringConvertible]? = nil
public let body: Never? = nil

public init(
Expand Down
106 changes: 106 additions & 0 deletions Sources/AnthropicSwiftSDK/API/Admin/APIKeys.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
//
// APIKeys.swift
// AnthropicSwiftSDK
//
// Created by 伊藤史 on 2024/11/28.
//
import Foundation

public struct APIKeys {
private let apiKey: String
private let session: URLSession

init(adminAPIKey: String, session: URLSession) {
self.apiKey = adminAPIKey
self.session = session
}

public func get(apiKeyId: String) async throws -> APIKeyResponse {
try await get(
apiKeyId: apiKeyId,
anthropicHeaderProvider: DefaultAnthropicHeaderProvider(),
authenticationHeaderProvider: APIKeyAuthenticationHeaderProvider(apiKey: apiKey)
)
}

public func get(
apiKeyId: String,
anthropicHeaderProvider: AnthropicHeaderProvider,
authenticationHeaderProvider: AuthenticationHeaderProvider
) async throws -> APIKeyResponse {
let client = APIClient(
session: session,
anthropicHeaderProvider: anthropicHeaderProvider,
authenticationHeaderProvider: authenticationHeaderProvider
)

return try await client.send(request: GetAPIKeyRequest(apiKeyId: apiKeyId))
}

public func list(
limit: Int = 20,
beforeId: String? = nil,
afterId: String? = nil,
status: APIKeyStatus? = nil,
workspaceId: String? = nil,
createdByUserId: String? = nil
) async throws -> ObjectListResponse<APIKeyResponse> {
fatalError("not implemented")
}

public func list(
limit: Int = 20,
beforeId: String? = nil,
afterId: String? = nil,
status: APIKeyStatus? = nil,
workspaceId: String? = nil,
createdByUserId: String? = nil,
anthropicHeaderProvider: AnthropicHeaderProvider,
authenticationHeaderProvider: AuthenticationHeaderProvider
) async throws -> ObjectListResponse<APIKeyResponse> {
try await list(
session: session,
type: .apiKey,
limit: limit,
beforeId: beforeId,
afterId: afterId,
anthropicHeaderProvider: anthropicHeaderProvider,
authenticationHeaderProvider: authenticationHeaderProvider
)
}

public func update(apiKeyId: String, name: String, status: APIKeyStatus? = nil) async throws -> APIKeyResponse {
try await update(
apiKeyId: apiKeyId,
name: name,
status: status,
anthropicHeaderProvider: DefaultAnthropicHeaderProvider(),
authenticationHeaderProvider: APIKeyAuthenticationHeaderProvider(apiKey: apiKey)
)
}

public func update(
apiKeyId: String,
name: String,
status: APIKeyStatus? = nil,
anthropicHeaderProvider: AnthropicHeaderProvider,
authenticationHeaderProvider: AuthenticationHeaderProvider
) async throws -> APIKeyResponse {
let client = APIClient(
session: session,
anthropicHeaderProvider: anthropicHeaderProvider,
authenticationHeaderProvider: authenticationHeaderProvider
)

return try await client.send(
request: UpdateAPIKeyRequest(
body: .init(name: name, status: status),
apiKeyId: apiKeyId
)
)
}
}

extension APIKeys: ObjectListable {
typealias Object = APIKeyResponse
}
23 changes: 23 additions & 0 deletions Sources/AnthropicSwiftSDK/API/Admin/Admin.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// Admin.swift
// AnthropicSwiftSDK
//
// Created by 伊藤史 on 2024/11/28.
//
import Foundation

public struct Admin {
public let organizationMembers: OrganizationMembers
public let organizationInvites: OrganizationInvites
public let workspaces: Workspaces
public let workspaceMembers: WorkspaceMembers
public let apiKeys: APIKeys

public init(adminAPIKey apiKey: String, session: URLSession = .shared) {
self.organizationMembers = OrganizationMembers(adminAPIKey: apiKey, session: session)
self.organizationInvites = OrganizationInvites(adminAPIKey: apiKey, session: session)
self.workspaces = Workspaces(adminAPIKey: apiKey, session: session)
self.workspaceMembers = WorkspaceMembers(adminAPIKey: apiKey, session: session)
self.apiKeys = APIKeys(adminAPIKey: apiKey, session: session)
}
}
Loading

0 comments on commit 30e7e34

Please sign in to comment.