Skip to content

Commit 6525635

Browse files
authored
📦 Add Files gRPC service proto and sync usage.proto/CI workflow updates (#127)
### New: `src/xAI.Protocol/files.proto` Adds the `Files` gRPC service for managing uploaded files: - `UploadFile` (client streaming): upload in chunks up to 5 MB; 512 MB total cap; first message must be `init` with filename and optional TTL - `ListFiles`: paginated listing, sortable by created date, filename, or size - `RetrieveFile`: fetch file metadata by ID - `DeleteFile`: delete a file by ID - `RetrieveFileContent` (server streaming): download raw bytes or extracted text in up to 5 MB chunks ### Updated: `src/xAI.Protocol/usage.proto` - Removed `csharp_namespace` option (synced from upstream) - Marked `num_sources_used` as deprecated (live search feature deprecated upstream) - Added `cost_in_usd_ticks` (optional `int64`): full price paid in USD ticks; 1 USD = 10,000,000,000 ticks ### Updated: CI Workflows (`.github/workflows/dotnet-env.yml`, `includes.yml`) - Replaced hard-coded `GH_TOKEN` references with `DEVLOOPED_TOKEN || GH_TOKEN` via a `PR_TOKEN` job-level env var - Added conditional `✍ pull request` step guard (`if: env.PR_TOKEN != ''`) - Added `⚠️ skip pull request` warning step when neither token is configured ### Updated: `src/xAI.Tests/ChatClientTests.cs` - Added `using File = System.IO.File;` alias to resolve ambiguity with the new `File` proto message type
1 parent efb4e33 commit 6525635

6 files changed

Lines changed: 222 additions & 14 deletions

File tree

.github/workflows/dotnet-env.yml

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ on:
1010
jobs:
1111
which-dotnet:
1212
runs-on: ubuntu-latest
13+
env:
14+
PR_TOKEN: ${{ secrets.DEVLOOPED_TOKEN || secrets.GH_TOKEN }}
1315
permissions:
1416
contents: write
1517
pull-requests: write
@@ -20,18 +22,19 @@ jobs:
2022
with:
2123
name: ${{ secrets.BOT_NAME }}
2224
email: ${{ secrets.BOT_EMAIL }}
23-
gh_token: ${{ secrets.GH_TOKEN }}
25+
gh_token: ${{ env.PR_TOKEN }}
2426
github_token: ${{ secrets.GITHUB_TOKEN }}
2527

2628
- name: 🤘 checkout
2729
uses: actions/checkout@v4
2830
with:
29-
token: ${{ env.GH_TOKEN }}
31+
token: ${{ env.PR_TOKEN || github.token }}
3032

3133
- name: 🤌 dotnet
3234
uses: devlooped/actions-which-dotnet@v1
3335

3436
- name: ✍ pull request
37+
if: env.PR_TOKEN != ''
3538
uses: peter-evans/create-pull-request@v7
3639
with:
3740
base: main
@@ -41,4 +44,9 @@ jobs:
4144
title: "⚙ Update dotnet versions"
4245
body: "Update dotnet versions"
4346
commit-message: "Update dotnet versions"
44-
token: ${{ env.GH_TOKEN }}
47+
token: ${{ env.PR_TOKEN }}
48+
49+
- name: ⚠️ skip pull request
50+
if: env.PR_TOKEN == ''
51+
shell: bash
52+
run: echo "::warning::Skipping PR creation because neither DEVLOOPED_TOKEN nor GH_TOKEN is configured. GITHUB_TOKEN cannot create pull requests in this repository."

.github/workflows/includes.yml

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ on:
1212
jobs:
1313
includes:
1414
runs-on: ubuntu-latest
15+
env:
16+
PR_TOKEN: ${{ secrets.DEVLOOPED_TOKEN || secrets.GH_TOKEN }}
1517
permissions:
1618
contents: write
1719
pull-requests: write
@@ -21,13 +23,13 @@ jobs:
2123
with:
2224
name: ${{ secrets.BOT_NAME }}
2325
email: ${{ secrets.BOT_EMAIL }}
24-
gh_token: ${{ secrets.GH_TOKEN }}
26+
gh_token: ${{ env.PR_TOKEN }}
2527
github_token: ${{ secrets.GITHUB_TOKEN }}
2628

2729
- name: 🤘 checkout
2830
uses: actions/checkout@v4
2931
with:
30-
token: ${{ env.GH_TOKEN }}
32+
token: ${{ env.PR_TOKEN || github.token }}
3133

3234
- name: +Mᐁ includes
3335
uses: devlooped/actions-includes@v1
@@ -50,6 +52,7 @@ jobs:
5052
((get-content -raw $file) -replace '\$product\$',$product).trim() | set-content $file
5153
5254
- name: ✍ pull request
55+
if: env.PR_TOKEN != ''
5356
uses: peter-evans/create-pull-request@v8
5457
with:
5558
add-paths: |
@@ -64,4 +67,9 @@ jobs:
6467
commit-message: +Mᐁ includes
6568
title: +Mᐁ includes
6669
body: +Mᐁ includes
67-
token: ${{ env.GH_TOKEN }}
70+
token: ${{ env.PR_TOKEN }}
71+
72+
- name: ⚠️ skip pull request
73+
if: env.PR_TOKEN == ''
74+
shell: bash
75+
run: echo "::warning::Skipping PR creation because neither DEVLOOPED_TOKEN nor GH_TOKEN is configured. GITHUB_TOKEN cannot create pull requests in this repository."

.netconfig

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
[file ".github/workflows/dotnet-env.yml"]
5757
url = https://github.com/devlooped/oss/blob/main/.github/workflows/dotnet-env.yml
5858
sha = 77e83f238196d2723640abef0c7b6f43994f9747
59-
etag = fcb9759a96966df40dcd24906fd328ddec05953b7e747a6bb8d0d1e4c3865274
59+
etag = 68c1e28f475ff9d05f985bf53a51fce6e64b24a8b75bd8e47119ff57309281f1
6060
weak
6161
[file ".github/workflows/dotnet-file.yml"]
6262
url = https://github.com/devlooped/oss/blob/main/.github/workflows/dotnet-file.yml
@@ -66,7 +66,7 @@
6666
[file ".github/workflows/includes.yml"]
6767
url = https://github.com/devlooped/oss/blob/main/.github/workflows/includes.yml
6868
sha = 06628725a6303bb8c4cf3076a384fc982a91bc0b
69-
etag = 478f91d4126230e57cc601382da1ba23f9daa054645b4af89800d8dd862e64fd
69+
etag = 5e6a10be141ee629201bfad01eae09b5c36a67f541ec7ab411ae400b5d73de1d
7070
weak
7171
[file ".github/workflows/publish.yml"]
7272
url = https://github.com/devlooped/oss/blob/main/.github/workflows/publish.yml
@@ -193,7 +193,7 @@
193193
[file "src/xAI.Protocol/usage.proto"]
194194
url = https://github.com/xai-org/xai-proto/blob/main/proto/xai/api/v1/usage.proto
195195
sha = 362749661fa2d340d234ad372fab920538897821
196-
etag = bd66823dfde1a2fff714d3712104bef886baf22da9f94c7505acee37d3ff67fd
196+
etag = 9fcf9731a547857d554e968968aedc3016fc5210e0a45d5b59d75a03b816a81e
197197
weak
198198
[file "src/xAI.Protocol/video.proto"]
199199
url = https://github.com/xai-org/xai-proto/blob/main/proto/xai/api/v1/video.proto
@@ -231,3 +231,7 @@
231231
sha = e616d89d9537c4b8ccf1c20dd277ab82104167c4
232232
etag = 6ee650d118a57494d3545d54718ccaa5257b09d54504e9c21514fe596edd9678
233233
weak
234+
[file "src/xAI.Protocol/files.proto"]
235+
url = https://github.com/xai-org/xai-proto/blob/main/proto/xai/api/v1/files.proto
236+
etag = 4c91f851b288a225acfc1173f2f8853a1b550da1e97d2fdcec99debb5f8fac43
237+
weak

src/xAI.Protocol/files.proto

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
syntax = "proto3";
2+
3+
package xai_api;
4+
5+
import "google/protobuf/timestamp.proto";
6+
7+
// Service for uploading, listing, retrieving, and deleting files.
8+
//
9+
// Files are referenced by the `id` returned on upload (e.g. when attaching
10+
// to chat completions). Maximum file size is 512 MB.
11+
service Files {
12+
// Upload a file. The first stream message MUST set `chunk = init`
13+
// (filename + optional TTL); subsequent messages MUST set `chunk = data`
14+
// with file bytes in order. Recommended chunk size up to 5 MB; total
15+
// size capped at 512 MB. Returns the new file's metadata.
16+
//
17+
// Errors: INVALID_ARGUMENT (missing/misplaced init, bad filename, bad
18+
// TTL), RESOURCE_EXHAUSTED (over 512 MB).
19+
rpc UploadFile(stream UploadFileChunk) returns (File) {}
20+
21+
// List file metadata, paginated and sorted. Returns metadata only — use
22+
// `RetrieveFileContent` to download bytes.
23+
rpc ListFiles(ListFilesRequest) returns (ListFilesResponse) {}
24+
25+
// Get metadata for one file. Returns metadata only — use
26+
// `RetrieveFileContent` to download bytes. Errors NOT_FOUND if no
27+
// accessible file with that id (including already-deleted).
28+
rpc RetrieveFile(RetrieveFileRequest) returns (File) {}
29+
30+
// Delete a file. After success it stops appearing in `ListFiles` /
31+
// `RetrieveFile` and is no longer downloadable. Errors NOT_FOUND if no
32+
// accessible file with that id (including already-deleted).
33+
rpc DeleteFile(DeleteFileRequest) returns (DeleteFileResponse) {}
34+
35+
// Stream the file's contents in chunks of up to 5 MB, in order.
36+
// Concatenate `data` from every chunk to reconstruct the file.
37+
rpc RetrieveFileContent(RetrieveFileContentRequest) returns (stream FileContentChunk) {}
38+
}
39+
40+
// First stream message of an `UploadFile` call. Sent exactly once, before
41+
// any data chunks.
42+
message UploadFileInit {
43+
// Filename (max 255 Unicode chars). Must not contain ASCII control
44+
// chars, line terminators (CR/LF/NEL/U+2028/U+2029), null bytes, `"`,
45+
// `;`, or `\` — these would break `Content-Disposition` headers.
46+
string name = 1;
47+
48+
// Optional TTL in seconds, measured from upload time. Must be in
49+
// [3600, 2592000] (1h to 30d) inclusive — 0 and out-of-range values are
50+
// rejected. Unset means no expiration.
51+
optional int64 expires_after = 2;
52+
}
53+
54+
// One message in an `UploadFile` stream: either `init` (required first) or
55+
// a `data` chunk.
56+
message UploadFileChunk {
57+
oneof chunk {
58+
// Information about the file being uploaded.
59+
UploadFileInit init = 1;
60+
// Up to ~5 MB per chunk recommended; cumulative size capped at 512 MB.
61+
bytes data = 2;
62+
}
63+
}
64+
65+
// Metadata for an uploaded file.
66+
message File {
67+
// Size in bytes.
68+
int64 size = 1;
69+
70+
// UTC timestamp the file was uploaded.
71+
google.protobuf.Timestamp created_at = 2;
72+
73+
// UTC timestamp the file will be auto-deleted at. Present only if the
74+
// upload set `UploadFileInit.expires_after`.
75+
optional google.protobuf.Timestamp expires_at = 3;
76+
77+
// Filename as supplied at upload.
78+
string filename = 4;
79+
80+
// Opaque server-assigned ID (e.g. `file_<uuid>`). Use this in
81+
// `RetrieveFile`, `DeleteFile`, `RetrieveFileContent`, and other xAI
82+
// APIs that accept a file reference.
83+
string id = 5;
84+
85+
reserved 6;
86+
}
87+
88+
// Sort direction for list-style RPCs.
89+
enum Ordering {
90+
ASCENDING = 0;
91+
DESCENDING = 1;
92+
}
93+
94+
// Field to sort `ListFiles` results by.
95+
enum FilesSortBy {
96+
// Default.
97+
FILES_SORT_BY_CREATED_AT = 0;
98+
// Case-sensitive lexicographic order.
99+
FILES_SORT_BY_FILENAME = 1;
100+
FILES_SORT_BY_SIZE = 2;
101+
}
102+
103+
// Request message for `Files.ListFiles`.
104+
message ListFilesRequest {
105+
// Page size, in [1, 100]. Values <= 0 default to 100; values > 100 are
106+
// clamped.
107+
int32 limit = 1;
108+
109+
// Sort direction. Defaults to ASCENDING.
110+
Ordering order = 2;
111+
112+
// Opaque token from a prior `ListFilesResponse.pagination_token`, used to
113+
// resume listing where the previous page left off. Omit on the first
114+
// call. Use the same `order` and `sort_by` as the request that produced
115+
// the token; otherwise paging behavior is undefined. Treat as opaque;
116+
// format may change.
117+
optional string pagination_token = 3;
118+
119+
// Sort field. Defaults to `FILES_SORT_BY_CREATED_AT`.
120+
optional FilesSortBy sort_by = 4;
121+
}
122+
123+
// Response message for `Files.ListFiles`.
124+
message ListFilesResponse {
125+
// List of file metadata.
126+
repeated File data = 1;
127+
128+
// Token for the next page; absent on the final page.
129+
optional string pagination_token = 2;
130+
}
131+
132+
// Request message for `Files.RetrieveFile`.
133+
message RetrieveFileRequest {
134+
// The ID of the file to use for this request.
135+
string file_id = 1;
136+
}
137+
138+
// Request message for `Files.DeleteFile`.
139+
message DeleteFileRequest {
140+
// The ID of the file to use for this request.
141+
string file_id = 1;
142+
}
143+
144+
// Response message for `Files.DeleteFile`.
145+
message DeleteFileResponse {
146+
// Echoes the deleted file's id.
147+
string id = 1;
148+
149+
// Always true on success (failures surface as gRPC errors).
150+
bool deleted = 2;
151+
}
152+
153+
// Which representation of a file to download.
154+
enum DownloadFormat {
155+
// Treated as `DOWNLOAD_FORMAT_ORIGINAL` (the default when `format` is
156+
// unset).
157+
DOWNLOAD_FORMAT_UNKNOWN = 0;
158+
// Raw bytes as uploaded.
159+
DOWNLOAD_FORMAT_ORIGINAL = 1;
160+
// Text extracted from the file (e.g. PDF/DOCX). Only works for file
161+
// types with a server-side text-extraction pass; fails otherwise.
162+
DOWNLOAD_FORMAT_TEXT = 2;
163+
}
164+
165+
// Request message for `Files.RetrieveFileContent`.
166+
message RetrieveFileContentRequest {
167+
// The ID of the file to download.
168+
string file_id = 1;
169+
170+
// Format of the downloaded content.
171+
// ORIGINAL: raw file bytes (default).
172+
// TEXT: extracted/converted text content.
173+
optional DownloadFormat format = 2;
174+
}
175+
176+
// One chunk of a `RetrieveFileContent` stream.
177+
message FileContentChunk {
178+
// Up to 5 MB of file bytes. Final/intermediate chunks may be smaller.
179+
bytes data = 1;
180+
}

src/xAI.Protocol/usage.proto

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
syntax = "proto3";
2-
option csharp_namespace = "xAI.Protocol";
32

43
package xai_api;
54

@@ -28,14 +27,22 @@ message SamplingUsage {
2827
// Total number of image tokens in the prompt.
2928
int32 prompt_image_tokens = 5;
3029

31-
// Number of individual live search sources used.
32-
// Only applicable when live search is enabled.
33-
// e.g. If a live search query returns citations from both X and Web and news sources, this will be 3.
34-
// If it returns citations from only X, this will be 1.
30+
// [DEPRECATED - live search feature has been deprecated so this field is
31+
// redundant] Number of individual live search sources used. Only applicable
32+
// when live search is enabled. e.g. If a live search query returns citations
33+
// from both X and Web and news sources, this will be 3. If it returns
34+
// citations from only X, this will be 1.
3535
int32 num_sources_used = 8;
3636

3737
// List of server side tools called.
3838
repeated ServerSideTool server_side_tools_used = 9;
39+
40+
// Full price paid by the user for this request, in USD ticks.
41+
// For requests with server-side tools, this is the sum of token cost and
42+
// server-side tool cost. Also used for image gen, video gen, etc.
43+
// 1 USD = 10,000,000,000 ticks (i.e. 1 tick = 1e-10 USD).
44+
// To convert to dollars, divide by 10,000,000,000.
45+
optional int64 cost_in_usd_ticks = 11;
3946
}
4047

4148
// Usage of embedding models.

src/xAI.Tests/ChatClientTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using xAI.Protocol;
99
using static ConfigurationExtensions;
1010
using Chat = Devlooped.Extensions.AI.Chat;
11+
using File = System.IO.File;
1112

1213
namespace xAI.Tests;
1314

0 commit comments

Comments
 (0)