Skip to content
This repository was archived by the owner on Jul 4, 2025. It is now read-only.

Commit 8dde05c

Browse files
authored
feat: add files api (#1781)
* feat: add files api * add backward support * add db support * fix link issue on windows
1 parent 2ee1e81 commit 8dde05c

File tree

17 files changed

+1046
-25
lines changed

17 files changed

+1046
-25
lines changed

engine/common/file.h

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
#pragma once
2+
3+
#include <string>
4+
#include "common/json_serializable.h"
5+
6+
namespace OpenAi {
7+
/**
8+
* The File object represents a document that has been uploaded to OpenAI.
9+
*/
10+
struct File : public JsonSerializable {
11+
/**
12+
* The file identifier, which can be referenced in the API endpoints.
13+
*/
14+
std::string id;
15+
16+
/**
17+
* The object type, which is always file.
18+
*/
19+
std::string object = "file";
20+
21+
/**
22+
* The size of the file, in bytes.
23+
*/
24+
uint64_t bytes;
25+
26+
/**
27+
* The Unix timestamp (in seconds) for when the file was created.
28+
*/
29+
uint32_t created_at;
30+
31+
/**
32+
* The name of the file.
33+
*/
34+
std::string filename;
35+
36+
/**
37+
* The intended purpose of the file. Supported values are assistants,
38+
* assistants_output, batch, batch_output, fine-tune, fine-tune-results
39+
* and vision.
40+
*/
41+
std::string purpose;
42+
43+
~File() = default;
44+
45+
static cpp::result<File, std::string> FromJson(const Json::Value& json) {
46+
File file;
47+
48+
file.id = std::move(json["id"].asString());
49+
file.object = "file";
50+
file.bytes = json["bytes"].asUInt64();
51+
file.created_at = json["created_at"].asUInt();
52+
file.filename = std::move(json["filename"].asString());
53+
file.purpose = std::move(json["purpose"].asString());
54+
55+
return file;
56+
}
57+
58+
cpp::result<Json::Value, std::string> ToJson() {
59+
Json::Value root;
60+
61+
root["id"] = id;
62+
root["object"] = object;
63+
root["bytes"] = bytes;
64+
root["created_at"] = created_at;
65+
root["filename"] = filename;
66+
root["purpose"] = purpose;
67+
68+
return root;
69+
}
70+
};
71+
} // namespace OpenAi

engine/common/message.h

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,20 @@
1919

2020
namespace OpenAi {
2121

22+
inline std::string ExtractFileId(const std::string& path) {
23+
// Handle both forward and backward slashes
24+
auto last_slash = path.find_last_of("/\\");
25+
if (last_slash == std::string::npos)
26+
return "";
27+
28+
auto filename = path.substr(last_slash + 1);
29+
auto dot_pos = filename.find('.');
30+
if (dot_pos == std::string::npos)
31+
return "";
32+
33+
return filename.substr(0, dot_pos);
34+
}
35+
2236
// Represents a message within a thread.
2337
struct Message : JsonSerializable {
2438
Message() = default;
@@ -70,6 +84,12 @@ struct Message : JsonSerializable {
7084
// Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format. Keys can be a maximum of 64 characters long and values can be a maximum of 512 characters long.
7185
Cortex::VariantMap metadata;
7286

87+
// deprecated. remove in the future
88+
std::optional<std::string> attach_filename;
89+
std::optional<uint64_t> size;
90+
std::optional<std::string> rel_path;
91+
// end deprecated
92+
7393
static cpp::result<Message, std::string> FromJsonString(
7494
std::string&& json_str) {
7595
Json::Value root;
@@ -98,7 +118,6 @@ struct Message : JsonSerializable {
98118
message.completed_at = root["completed_at"].asUInt();
99119
message.incomplete_at = root["incomplete_at"].asUInt();
100120
message.role = RoleFromString(std::move(root["role"].asString()));
101-
message.content = ParseContents(std::move(root["content"])).value();
102121

103122
message.assistant_id = std::move(root["assistant_id"].asString());
104123
message.run_id = std::move(root["run_id"].asString());
@@ -114,6 +133,54 @@ struct Message : JsonSerializable {
114133
}
115134
}
116135

136+
if (root.isMember("content")) {
137+
if (root["content"].isArray() && !root["content"].empty()) {
138+
if (root["content"][0]["type"].asString() == "text") {
139+
message.content = ParseContents(std::move(root["content"])).value();
140+
} else {
141+
// deprecated, for supporting jan and should be removed in the future
142+
// check if annotations is empty
143+
if (!root["content"][0]["text"]["annotations"].empty()) {
144+
// parse attachment
145+
Json::Value attachments_json_array{Json::arrayValue};
146+
Json::Value attachment;
147+
attachment["file_id"] = ExtractFileId(
148+
root["content"][0]["text"]["annotations"][0].asString());
149+
150+
Json::Value tools_json_array{Json::arrayValue};
151+
Json::Value tool;
152+
tool["type"] = "file_search";
153+
tools_json_array.append(tool);
154+
155+
attachment["tools"] = tools_json_array;
156+
attachment["file_id"] = attachments_json_array.append(attachment);
157+
158+
message.attachments =
159+
ParseAttachments(std::move(attachments_json_array)).value();
160+
161+
message.attach_filename =
162+
root["content"][0]["text"]["name"].asString();
163+
message.size = root["content"][0]["text"]["size"].asUInt64();
164+
message.rel_path =
165+
root["content"][0]["text"]["annotations"][0].asString();
166+
}
167+
168+
// parse content
169+
Json::Value contents_json_array{Json::arrayValue};
170+
Json::Value content;
171+
Json::Value content_text;
172+
Json::Value empty_annotations{Json::arrayValue};
173+
content["type"] = "text";
174+
content_text["value"] = root["content"][0]["text"]["value"];
175+
content_text["annotations"] = empty_annotations;
176+
content["text"] = content_text;
177+
contents_json_array.append(content);
178+
message.content =
179+
ParseContents(std::move(contents_json_array)).value();
180+
}
181+
}
182+
}
183+
117184
return message;
118185
} catch (const std::exception& e) {
119186
return cpp::fail(std::string("FromJsonString failed: ") + e.what());
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#pragma once
2+
3+
#include "common/file.h"
4+
#include "utils/result.hpp"
5+
6+
class FileRepository {
7+
public:
8+
virtual cpp::result<void, std::string> StoreFile(OpenAi::File& file_metadata,
9+
const char* content,
10+
uint64_t length) = 0;
11+
12+
virtual cpp::result<std::vector<OpenAi::File>, std::string> ListFiles(
13+
const std::string& purpose, uint8_t limit, const std::string& order,
14+
const std::string& after) const = 0;
15+
16+
virtual cpp::result<OpenAi::File, std::string> RetrieveFile(
17+
const std::string file_id) const = 0;
18+
19+
virtual cpp::result<std::pair<std::unique_ptr<char[]>, size_t>, std::string>
20+
RetrieveFileContent(const std::string& file_id) const = 0;
21+
22+
virtual cpp::result<std::pair<std::unique_ptr<char[]>, size_t>, std::string>
23+
RetrieveFileContentByPath(const std::string& path) const = 0;
24+
25+
virtual cpp::result<void, std::string> DeleteFileLocal(
26+
const std::string& file_id) = 0;
27+
28+
virtual ~FileRepository() = default;
29+
};

0 commit comments

Comments
 (0)