@@ -6,7 +6,6 @@ use axum::{
6
6
Json , Router , ServiceExt ,
7
7
} ;
8
8
use chroma_system:: System ;
9
- use chroma_types:: RawWhereFields ;
10
9
use chroma_types:: {
11
10
AddCollectionRecordsResponse , ChecklistResponse , Collection , CollectionConfiguration ,
12
11
CollectionMetadataUpdate , CollectionUuid , CountCollectionsRequest , CountCollectionsResponse ,
@@ -20,6 +19,7 @@ use chroma_types::{
20
19
UpdateCollectionConfiguration , UpdateCollectionRecordsResponse , UpdateCollectionResponse ,
21
20
UpdateMetadata , UpsertCollectionRecordsResponse ,
22
21
} ;
22
+ use chroma_types:: { ForkCollectionResponse , RawWhereFields } ;
23
23
use mdac:: { Rule , Scorecard , ScorecardTicket } ;
24
24
use opentelemetry:: global;
25
25
use opentelemetry:: metrics:: { Counter , Meter } ;
@@ -103,6 +103,7 @@ pub struct Metrics {
103
103
get_collection : Counter < u64 > ,
104
104
update_collection : Counter < u64 > ,
105
105
delete_collection : Counter < u64 > ,
106
+ fork_collection : Counter < u64 > ,
106
107
collection_add : Counter < u64 > ,
107
108
collection_update : Counter < u64 > ,
108
109
collection_upsert : Counter < u64 > ,
@@ -133,6 +134,7 @@ impl Metrics {
133
134
get_collection : meter. u64_counter ( "get_collection" ) . build ( ) ,
134
135
update_collection : meter. u64_counter ( "update_collection" ) . build ( ) ,
135
136
delete_collection : meter. u64_counter ( "delete_collection" ) . build ( ) ,
137
+ fork_collection : meter. u64_counter ( "fork_collection" ) . build ( ) ,
136
138
collection_add : meter. u64_counter ( "collection_add" ) . build ( ) ,
137
139
collection_update : meter. u64_counter ( "collection_update" ) . build ( ) ,
138
140
collection_upsert : meter. u64_counter ( "collection_upsert" ) . build ( ) ,
@@ -242,6 +244,10 @@ impl FrontendServer {
242
244
. put ( update_collection)
243
245
. delete ( delete_collection) ,
244
246
)
247
+ . route (
248
+ "/api/v2/tenants/{tenant}/databases/{database}/collections/{collection_id}/fork" ,
249
+ post ( fork_collection) ,
250
+ )
245
251
. route (
246
252
"/api/v2/tenants/{tenant}/databases/{database}/collections/{collection_id}/add" ,
247
253
post ( collection_add) ,
@@ -1106,6 +1112,70 @@ async fn delete_collection(
1106
1112
Ok ( Json ( UpdateCollectionResponse { } ) )
1107
1113
}
1108
1114
1115
+ #[ derive( Deserialize , Serialize , ToSchema , Debug , Clone ) ]
1116
+ pub struct ForkCollectionPayload {
1117
+ pub new_name : String ,
1118
+ }
1119
+
1120
+ /// Forks an existing collection.
1121
+ #[ utoipa:: path(
1122
+ post,
1123
+ path = "/api/v2/tenants/{tenant}/databases/{database}/collections/{collection_id}/fork" ,
1124
+ request_body = ForkCollectionPayload ,
1125
+ responses(
1126
+ ( status = 200 , description = "Collection forked successfully" , body = ForkCollectionResponse ) ,
1127
+ ( status = 401 , description = "Unauthorized" , body = ErrorResponse ) ,
1128
+ ( status = 404 , description = "Collection not found" , body = ErrorResponse ) ,
1129
+ ( status = 500 , description = "Server error" , body = ErrorResponse )
1130
+ ) ,
1131
+ params(
1132
+ ( "tenant" = String , Path , description = "Tenant ID" ) ,
1133
+ ( "database" = String , Path , description = "Database name" ) ,
1134
+ ( "collection_id" = String , Path , description = "UUID of the collection to update" )
1135
+ )
1136
+ ) ]
1137
+ async fn fork_collection (
1138
+ headers : HeaderMap ,
1139
+ Path ( ( tenant, database, collection_id) ) : Path < ( String , String , String ) > ,
1140
+ State ( mut server) : State < FrontendServer > ,
1141
+ Json ( payload) : Json < ForkCollectionPayload > ,
1142
+ ) -> Result < Json < ForkCollectionResponse > , ServerError > {
1143
+ server. metrics . fork_collection . add (
1144
+ 1 ,
1145
+ & [
1146
+ KeyValue :: new ( "tenant" , tenant. clone ( ) ) ,
1147
+ KeyValue :: new ( "collection_id" , collection_id. clone ( ) ) ,
1148
+ ] ,
1149
+ ) ;
1150
+ tracing:: info!(
1151
+ "Forking collection [{collection_id}] in database [{database}] for tenant [{tenant}]"
1152
+ ) ;
1153
+ server
1154
+ . authenticate_and_authorize (
1155
+ & headers,
1156
+ AuthzAction :: ForkCollection ,
1157
+ AuthzResource {
1158
+ tenant : Some ( tenant. clone ( ) ) ,
1159
+ database : Some ( database. clone ( ) ) ,
1160
+ collection : Some ( collection_id. clone ( ) ) ,
1161
+ } ,
1162
+ )
1163
+ . await ?;
1164
+ let _guard =
1165
+ server. scorecard_request ( & [ "op:fork_collection" , format ! ( "tenant:{}" , tenant) . as_str ( ) ] ) ;
1166
+ let collection_id =
1167
+ CollectionUuid :: from_str ( & collection_id) . map_err ( |_| ValidationError :: CollectionId ) ?;
1168
+
1169
+ let request = chroma_types:: ForkCollectionRequest :: try_new (
1170
+ tenant,
1171
+ database,
1172
+ collection_id,
1173
+ payload. new_name ,
1174
+ ) ?;
1175
+
1176
+ Ok ( Json ( server. frontend . fork_collection ( request) . await ?) )
1177
+ }
1178
+
1109
1179
#[ derive( Serialize , Deserialize , ToSchema , Debug , Clone ) ]
1110
1180
pub struct AddCollectionRecordsPayload {
1111
1181
ids : Vec < String > ,
0 commit comments