2525import com .google .gson .JsonElement ;
2626import com .google .gson .JsonObject ;
2727
28+ import java .lang .reflect .Constructor ;
29+ import java .lang .reflect .Method ;
2830import java .lang .reflect .Type ;
2931import java .util .ArrayList ;
3032import java .util .Arrays ;
3840/** A {@link TranslatorFactory} that creates a {@code RawTranslator} instance. */
3941public class NoopServingTranslatorFactory implements TranslatorFactory {
4042
43+ private static final Object LOCK = new Object ();
44+ private static Class <?> csvTranslatorClass ;
45+ private static Constructor <?> csvConstructor ;
46+ private static Method csvProcessInputMethod ;
47+ private static Method csvProcessOutputMethod ;
48+
4149 /** {@inheritDoc} */
4250 @ Override
4351 public Set <Pair <Type , Type >> getSupportedTypes () {
@@ -59,9 +67,40 @@ public <I, O> Translator<I, O> newInstance(
5967 static final class NoopServingTranslator implements Translator <Input , Output > {
6068
6169 private Batchifier batchifier ;
70+ private Object csvTranslator ;
71+ private Method csvProcessInput ;
72+ private Method csvProcessOutput ;
6273
6374 NoopServingTranslator (Batchifier batchifier ) {
6475 this .batchifier = batchifier ;
76+ initializeCsvTranslator ();
77+ }
78+
79+ private void initializeCsvTranslator () {
80+ try {
81+ // Use cached reflection objects if available
82+ if (csvTranslatorClass == null ) {
83+ synchronized (LOCK ) {
84+ if (csvTranslatorClass == null ) {
85+ csvTranslatorClass =
86+ Class .forName ("ai.djl.basicdataset.tabular.CsvTranslator" );
87+ csvConstructor = csvTranslatorClass .getConstructor (Map .class );
88+ csvProcessInputMethod =
89+ csvTranslatorClass .getMethod (
90+ "processInput" , TranslatorContext .class , String .class );
91+ csvProcessOutputMethod =
92+ csvTranslatorClass .getMethod (
93+ "processOutput" , TranslatorContext .class , NDList .class );
94+ }
95+ }
96+ }
97+ csvTranslator = csvConstructor .newInstance (Collections .emptyMap ());
98+ this .csvProcessInput = NoopServingTranslatorFactory .csvProcessInputMethod ;
99+ this .csvProcessOutput = NoopServingTranslatorFactory .csvProcessOutputMethod ;
100+ } catch (ReflectiveOperationException e ) {
101+ // CSV translator not available - silently continue without CSV support
102+ csvTranslator = null ;
103+ }
65104 }
66105
67106 /** {@inheritDoc} */
@@ -82,7 +121,7 @@ public NDList processInput(TranslatorContext ctx, Input input) throws TranslateE
82121 if (pos > 0 ) {
83122 contentType = contentType .substring (0 , pos );
84123 }
85- if ("application/json" .equals (contentType )) {
124+ if ("application/json" .equalsIgnoreCase (contentType )) {
86125 String data = input .getData ().getAsString ();
87126 JsonElement element = JsonUtils .GSON .fromJson (data , JsonElement .class );
88127 if (element .isJsonObject ()) {
@@ -97,6 +136,12 @@ public NDList processInput(TranslatorContext ctx, Input input) throws TranslateE
97136 } else {
98137 throw new TranslateException ("Input is not a supported json format" );
99138 }
139+ } else if ("text/csv" .equalsIgnoreCase (contentType )) {
140+ if (csvTranslator == null ) {
141+ throw new TranslateException (
142+ "CSV support not available. Add basicdataset dependency." );
143+ }
144+ return processCsvInput (ctx , input .getData ().getAsString ());
100145 }
101146 }
102147
@@ -127,6 +172,14 @@ public Output processOutput(TranslatorContext ctx, NDList list) {
127172 || "tensor/safetensors" .equalsIgnoreCase (contentType )) {
128173 output .add (list .encode (NDList .Encoding .SAFETENSORS ));
129174 output .addProperty ("Content-Type" , "tensor/safetensors" );
175+ } else if ("text/csv" .equalsIgnoreCase (accept )) {
176+ if (csvTranslator == null ) {
177+ throw new IllegalArgumentException (
178+ "CSV support not available. Add basicdataset dependency." );
179+ }
180+ String csvOutput = processCsvOutput (ctx , list );
181+ output .add (csvOutput );
182+ output .addProperty ("Content-Type" , "text/csv" );
130183 } else if ("application/json" .equalsIgnoreCase (accept )
131184 || "application/json" .equalsIgnoreCase (contentType )) {
132185 List <Object > ret ;
@@ -141,13 +194,39 @@ public Output processOutput(TranslatorContext ctx, NDList list) {
141194 Map <String , List <Object >> map = new ConcurrentHashMap <>();
142195 map .put ("predictions" , ret );
143196 output .add ("predictions" , BytesSupplier .wrapAsJson (map ));
197+
144198 } else {
145199 output .add (list .encode ());
146200 output .addProperty ("Content-Type" , "tensor/ndlist" );
147201 }
148202 return output ;
149203 }
150204
205+ // --- CSV helper methods ---
206+
207+ private NDList processCsvInput (TranslatorContext ctx , String csvData )
208+ throws TranslateException {
209+ try {
210+ return (NDList ) csvProcessInput .invoke (csvTranslator , ctx , csvData );
211+ } catch (ReflectiveOperationException e ) {
212+ Throwable cause = e .getCause ();
213+ if (cause instanceof TranslateException ) {
214+ TranslateException te = (TranslateException ) cause ;
215+ te .addSuppressed (e );
216+ throw te ;
217+ }
218+ throw new TranslateException ("Failed to process CSV input" , e );
219+ }
220+ }
221+
222+ private String processCsvOutput (TranslatorContext ctx , NDList list ) {
223+ try {
224+ return (String ) csvProcessOutput .invoke (csvTranslator , ctx , list );
225+ } catch (ReflectiveOperationException e ) {
226+ throw new IllegalStateException ("Failed to process CSV output" , e );
227+ }
228+ }
229+
151230 private NDList toNDList (NDManager manager , JsonElement element ) {
152231 JsonElement e = element .getAsJsonArray ().get (0 );
153232 if (e .isJsonArray ()) {
0 commit comments