@@ -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
UpdateCollectionRecordsResponse , UpdateCollectionResponse , UpdateMetadata ,
21
20
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 } ;
@@ -93,6 +93,7 @@ pub struct Metrics {
93
93
get_collection : Counter < u64 > ,
94
94
update_collection : Counter < u64 > ,
95
95
delete_collection : Counter < u64 > ,
96
+ fork_collection : Counter < u64 > ,
96
97
collection_add : Counter < u64 > ,
97
98
collection_update : Counter < u64 > ,
98
99
collection_upsert : Counter < u64 > ,
@@ -123,6 +124,7 @@ impl Metrics {
123
124
get_collection : meter. u64_counter ( "get_collection" ) . build ( ) ,
124
125
update_collection : meter. u64_counter ( "update_collection" ) . build ( ) ,
125
126
delete_collection : meter. u64_counter ( "delete_collection" ) . build ( ) ,
127
+ fork_collection : meter. u64_counter ( "fork_collection" ) . build ( ) ,
126
128
collection_add : meter. u64_counter ( "collection_add" ) . build ( ) ,
127
129
collection_update : meter. u64_counter ( "collection_update" ) . build ( ) ,
128
130
collection_upsert : meter. u64_counter ( "collection_upsert" ) . build ( ) ,
@@ -232,6 +234,10 @@ impl FrontendServer {
232
234
. put ( update_collection)
233
235
. delete ( delete_collection) ,
234
236
)
237
+ . route (
238
+ "/api/v2/tenants/{tenant}/databases/{database}/collections/{collection_id}/fork" ,
239
+ post ( fork_collection) ,
240
+ )
235
241
. route (
236
242
"/api/v2/tenants/{tenant}/databases/{database}/collections/{collection_id}/add" ,
237
243
post ( collection_add) ,
@@ -1088,6 +1094,70 @@ async fn delete_collection(
1088
1094
Ok ( Json ( UpdateCollectionResponse { } ) )
1089
1095
}
1090
1096
1097
+ #[ derive( Deserialize , Serialize , ToSchema , Debug , Clone ) ]
1098
+ pub struct ForkCollectionPayload {
1099
+ pub new_name : String ,
1100
+ }
1101
+
1102
+ /// Forks an existing collection.
1103
+ #[ utoipa:: path(
1104
+ post,
1105
+ path = "/api/v2/tenants/{tenant}/databases/{database}/collections/{collection_id}/fork" ,
1106
+ request_body = ForkCollectionPayload ,
1107
+ responses(
1108
+ ( status = 200 , description = "Collection forked successfully" , body = ForkCollectionResponse ) ,
1109
+ ( status = 401 , description = "Unauthorized" , body = ErrorResponse ) ,
1110
+ ( status = 404 , description = "Collection not found" , body = ErrorResponse ) ,
1111
+ ( status = 500 , description = "Server error" , body = ErrorResponse )
1112
+ ) ,
1113
+ params(
1114
+ ( "tenant" = String , Path , description = "Tenant ID" ) ,
1115
+ ( "database" = String , Path , description = "Database name" ) ,
1116
+ ( "collection_id" = String , Path , description = "UUID of the collection to update" )
1117
+ )
1118
+ ) ]
1119
+ async fn fork_collection (
1120
+ headers : HeaderMap ,
1121
+ Path ( ( tenant, database, collection_id) ) : Path < ( String , String , String ) > ,
1122
+ State ( mut server) : State < FrontendServer > ,
1123
+ Json ( payload) : Json < ForkCollectionPayload > ,
1124
+ ) -> Result < Json < ForkCollectionResponse > , ServerError > {
1125
+ server. metrics . fork_collection . add (
1126
+ 1 ,
1127
+ & [
1128
+ KeyValue :: new ( "tenant" , tenant. clone ( ) ) ,
1129
+ KeyValue :: new ( "collection_id" , collection_id. clone ( ) ) ,
1130
+ ] ,
1131
+ ) ;
1132
+ tracing:: info!(
1133
+ "Forking collection [{collection_id}] in database [{database}] for tenant [{tenant}]"
1134
+ ) ;
1135
+ server
1136
+ . authenticate_and_authorize (
1137
+ & headers,
1138
+ AuthzAction :: ForkCollection ,
1139
+ AuthzResource {
1140
+ tenant : Some ( tenant. clone ( ) ) ,
1141
+ database : Some ( database. clone ( ) ) ,
1142
+ collection : Some ( collection_id. clone ( ) ) ,
1143
+ } ,
1144
+ )
1145
+ . await ?;
1146
+ let _guard =
1147
+ server. scorecard_request ( & [ "op:fork_collection" , format ! ( "tenant:{}" , tenant) . as_str ( ) ] ) ;
1148
+ let collection_id =
1149
+ CollectionUuid :: from_str ( & collection_id) . map_err ( |_| ValidationError :: CollectionId ) ?;
1150
+
1151
+ let request = chroma_types:: ForkCollectionRequest :: try_new (
1152
+ tenant,
1153
+ database,
1154
+ collection_id,
1155
+ payload. new_name ,
1156
+ ) ?;
1157
+
1158
+ Ok ( Json ( server. frontend . fork_collection ( request) . await ?) )
1159
+ }
1160
+
1091
1161
#[ derive( Serialize , Deserialize , ToSchema , Debug , Clone ) ]
1092
1162
pub struct AddCollectionRecordsPayload {
1093
1163
ids : Vec < String > ,
0 commit comments