2025-02-09 12:36:37 +01:00
using System.Text ;
using System.Text.Json ;
using AIStudio.Settings ;
2026-05-18 16:26:51 +02:00
using AIStudio.Settings.DataModel ;
2025-02-09 12:36:37 +01:00
using AIStudio.Tools.ERIClient.DataModel ;
2025-05-04 14:59:30 +02:00
using AIStudio.Tools.PluginSystem ;
2025-02-15 15:41:12 +01:00
using AIStudio.Tools.Services ;
2025-02-09 12:36:37 +01:00
namespace AIStudio.Tools.ERIClient ;
2025-03-11 13:57:47 +01:00
public class ERIClientV1 ( IERIDataSource dataSource ) : ERIClientBase ( dataSource ), IERIClient
2025-02-09 12:36:37 +01:00
{
2025-05-04 14:59:30 +02:00
private static string TB ( string fallbackEN ) => I18N . I . T ( fallbackEN , typeof ( ERIClientV1 ). Namespace , nameof ( ERIClientV1 ));
2025-02-09 12:36:37 +01:00
#region Implementation of IERIClient
public async Task < APIResponse < List < AuthScheme >>> GetAuthMethodsAsync ( CancellationToken cancellationToken = default )
{
2025-02-15 15:41:12 +01:00
try
2025-02-09 12:36:37 +01:00
{
2026-04-16 16:48:47 +02:00
using var response = await this . HttpClient . GetAsync ( "/auth/methods" , cancellationToken );
2025-02-15 15:41:12 +01:00
if (! response . IsSuccessStatusCode )
{
return new ()
{
Successful = false ,
2025-05-04 14:59:30 +02:00
Message = string . Format ( TB ( "Failed to retrieve the authentication methods: there was an issue communicating with the ERI server. Code: {0}, Reason: {1}" ), response . StatusCode , response . ReasonPhrase )
2025-02-15 15:41:12 +01:00
};
}
var authMethods = await response . Content . ReadFromJsonAsync < List < AuthScheme >>( JSON_OPTIONS , cancellationToken );
if ( authMethods is null )
{
return new ()
{
Successful = false ,
2025-05-04 14:59:30 +02:00
Message = TB ( "Failed to retrieve the authentication methods: the ERI server did not return a valid response." )
2025-02-15 15:41:12 +01:00
};
}
2025-02-09 12:36:37 +01:00
return new ()
{
2025-02-15 15:41:12 +01:00
Successful = true ,
Data = authMethods
2025-02-09 12:36:37 +01:00
};
}
2025-02-15 15:41:12 +01:00
catch ( TaskCanceledException )
2025-02-09 12:36:37 +01:00
{
return new ()
{
Successful = false ,
2025-05-04 14:59:30 +02:00
Message = TB ( "Failed to retrieve the authentication methods: the request was canceled either by the user or due to a timeout." )
2025-02-09 12:36:37 +01:00
};
}
2025-02-15 15:41:12 +01:00
catch ( Exception e )
2025-02-09 12:36:37 +01:00
{
2025-02-15 15:41:12 +01:00
return new ()
{
Successful = false ,
2025-05-04 14:59:30 +02:00
Message = string . Format ( TB ( "Failed to retrieve the authentication methods due to an exception: {0}" ), e . Message )
2025-02-15 15:41:12 +01:00
};
}
2025-02-09 12:36:37 +01:00
}
2025-03-11 13:57:47 +01:00
public async Task < APIResponse < AuthResponse >> AuthenticateAsync ( RustService rustService , string? temporarySecret = null , CancellationToken cancellationToken = default )
2025-02-09 12:36:37 +01:00
{
2025-02-15 15:41:12 +01:00
try
2025-02-09 12:36:37 +01:00
{
2026-04-16 16:48:47 +02:00
var authMethod = this . DataSource . AuthMethod ;
var username = this . DataSource . Username ;
switch ( this . DataSource . AuthMethod )
2025-02-15 15:41:12 +01:00
{
case AuthMethod . NONE :
using ( var request = new HttpRequestMessage ( HttpMethod . Post , $"auth?authMethod={authMethod}" ))
2025-02-09 12:36:37 +01:00
{
2026-04-16 16:48:47 +02:00
using var noneAuthResponse = await this . HttpClient . SendAsync ( request , cancellationToken );
2025-02-15 15:41:12 +01:00
if (! noneAuthResponse . IsSuccessStatusCode )
{
return new ()
{
Successful = false ,
2025-05-04 14:59:30 +02:00
Message = string . Format ( TB ( "Failed to authenticate with the ERI server. Code: {0}, Reason: {1}" ), noneAuthResponse . StatusCode , noneAuthResponse . ReasonPhrase )
2025-02-15 15:41:12 +01:00
};
}
var noneAuthResult = await noneAuthResponse . Content . ReadFromJsonAsync < AuthResponse >( JSON_OPTIONS , cancellationToken );
if ( noneAuthResult == default )
{
return new ()
{
Successful = false ,
2025-05-04 14:59:30 +02:00
Message = TB ( "Failed to authenticate with the ERI server: the response was invalid." )
2025-02-15 15:41:12 +01:00
};
}
2026-04-16 16:48:47 +02:00
this . SecurityToken = noneAuthResult . Token ?? string . Empty ;
2025-02-09 12:36:37 +01:00
return new ()
{
2025-02-15 15:41:12 +01:00
Successful = true ,
Data = noneAuthResult
2025-02-09 12:36:37 +01:00
};
}
2025-02-15 15:41:12 +01:00
case AuthMethod . USERNAME_PASSWORD :
2026-05-18 16:26:51 +02:00
if ( this . DataSource . UsernamePasswordMode is DataSourceERIUsernamePasswordMode . OS_USERNAME_SHARED_PASSWORD )
{
username = await rustService . ReadUserName ();
if ( string . IsNullOrWhiteSpace ( username ))
{
return new ()
{
Successful = false ,
Message = TB ( "Failed to read the user's username from the operating system." )
};
}
}
2025-03-11 13:57:47 +01:00
string password ;
if ( string . IsNullOrWhiteSpace ( temporarySecret ))
2025-02-09 12:36:37 +01:00
{
2026-05-18 16:26:51 +02:00
var passwordResponse = await rustService . GetSecret ( this . DataSource , SecretStoreType . DATA_SOURCE );
2025-03-11 13:57:47 +01:00
if (! passwordResponse . Success )
2025-02-09 12:36:37 +01:00
{
2025-03-11 13:57:47 +01:00
return new ()
{
Successful = false ,
2025-05-04 14:59:30 +02:00
Message = TB ( "Failed to retrieve the password." )
2025-03-11 13:57:47 +01:00
};
}
password = await passwordResponse . Secret . Decrypt ( Program . ENCRYPTION );
2025-02-09 12:36:37 +01:00
}
2025-03-11 13:57:47 +01:00
else
password = temporarySecret ;
2025-02-09 12:36:37 +01:00
2025-02-15 15:41:12 +01:00
using ( var request = new HttpRequestMessage ( HttpMethod . Post , $"auth?authMethod={authMethod}" ))
2025-02-09 12:36:37 +01:00
{
2025-02-15 15:41:12 +01:00
// We must send both values inside the header. The username field is named 'user'.
// The password field is named 'password'.
request . Headers . Add ( "user" , username );
request . Headers . Add ( "password" , password );
2026-04-16 16:48:47 +02:00
using var usernamePasswordAuthResponse = await this . HttpClient . SendAsync ( request , cancellationToken );
2025-02-15 15:41:12 +01:00
if (! usernamePasswordAuthResponse . IsSuccessStatusCode )
{
return new ()
{
Successful = false ,
2025-05-04 14:59:30 +02:00
Message = string . Format ( TB ( "Failed to authenticate with the ERI server. Code: {0}, Reason: {1}" ), usernamePasswordAuthResponse . StatusCode , usernamePasswordAuthResponse . ReasonPhrase )
2025-02-15 15:41:12 +01:00
};
}
var usernamePasswordAuthResult = await usernamePasswordAuthResponse . Content . ReadFromJsonAsync < AuthResponse >( JSON_OPTIONS , cancellationToken );
if ( usernamePasswordAuthResult == default )
{
return new ()
{
Successful = false ,
2025-05-04 14:59:30 +02:00
Message = TB ( "Failed to authenticate with the server: the response was invalid." )
2025-02-15 15:41:12 +01:00
};
}
2026-04-16 16:48:47 +02:00
this . SecurityToken = usernamePasswordAuthResult . Token ?? string . Empty ;
2025-02-09 12:36:37 +01:00
return new ()
{
2025-02-15 15:41:12 +01:00
Successful = true ,
Data = usernamePasswordAuthResult
2025-02-09 12:36:37 +01:00
};
}
2025-02-15 15:41:12 +01:00
case AuthMethod . TOKEN :
2025-03-11 13:57:47 +01:00
string token ;
if ( string . IsNullOrWhiteSpace ( temporarySecret ))
2025-02-09 12:36:37 +01:00
{
2026-05-18 16:26:51 +02:00
var tokenResponse = await rustService . GetSecret ( this . DataSource , SecretStoreType . DATA_SOURCE );
2025-03-11 13:57:47 +01:00
if (! tokenResponse . Success )
2025-02-09 12:36:37 +01:00
{
2025-03-11 13:57:47 +01:00
return new ()
{
Successful = false ,
2025-05-04 14:59:30 +02:00
Message = TB ( "Failed to retrieve the access token." )
2025-03-11 13:57:47 +01:00
};
}
token = await tokenResponse . Secret . Decrypt ( Program . ENCRYPTION );
2025-02-09 12:36:37 +01:00
}
2025-03-11 13:57:47 +01:00
else
token = temporarySecret ;
2025-02-09 12:36:37 +01:00
2025-02-15 15:41:12 +01:00
using ( var request = new HttpRequestMessage ( HttpMethod . Post , $"auth?authMethod={authMethod}" ))
2025-02-09 12:36:37 +01:00
{
2025-02-15 15:41:12 +01:00
request . Headers . Add ( "Authorization" , $"Bearer {token}" );
2025-02-09 12:36:37 +01:00
2026-04-16 16:48:47 +02:00
using var tokenAuthResponse = await this . HttpClient . SendAsync ( request , cancellationToken );
2025-02-15 15:41:12 +01:00
if (! tokenAuthResponse . IsSuccessStatusCode )
2025-02-09 12:36:37 +01:00
{
2025-02-15 15:41:12 +01:00
return new ()
{
Successful = false ,
2025-05-04 14:59:30 +02:00
Message = string . Format ( TB ( "Failed to authenticate with the ERI server. Code: {0}, Reason: {1}" ), tokenAuthResponse . StatusCode , tokenAuthResponse . ReasonPhrase )
2025-02-15 15:41:12 +01:00
};
}
2025-02-09 12:36:37 +01:00
2025-02-15 15:41:12 +01:00
var tokenAuthResult = await tokenAuthResponse . Content . ReadFromJsonAsync < AuthResponse >( JSON_OPTIONS , cancellationToken );
if ( tokenAuthResult == default )
{
return new ()
{
Successful = false ,
2025-05-04 14:59:30 +02:00
Message = TB ( "Failed to authenticate with the ERI server: the response was invalid." )
2025-02-15 15:41:12 +01:00
};
}
2026-04-16 16:48:47 +02:00
this . SecurityToken = tokenAuthResult . Token ?? string . Empty ;
2025-02-09 12:36:37 +01:00
return new ()
{
2025-02-15 15:41:12 +01:00
Successful = true ,
Data = tokenAuthResult
2025-02-09 12:36:37 +01:00
};
}
2025-02-15 15:41:12 +01:00
default :
2026-04-16 16:48:47 +02:00
this . SecurityToken = string . Empty ;
2025-02-09 12:36:37 +01:00
return new ()
{
2025-02-15 15:41:12 +01:00
Successful = false ,
2025-05-04 14:59:30 +02:00
Message = TB ( "The authentication method is not supported yet." )
2025-02-09 12:36:37 +01:00
};
2025-02-15 15:41:12 +01:00
}
}
catch ( TaskCanceledException )
{
return new ()
{
Successful = false ,
2025-05-04 14:59:30 +02:00
Message = TB ( "Failed to authenticate with the ERI server: the request was canceled either by the user or due to a timeout." )
2025-02-15 15:41:12 +01:00
};
}
catch ( Exception e )
{
return new ()
{
Successful = false ,
2025-05-04 14:59:30 +02:00
Message = string . Format ( TB ( "Failed to authenticate with the ERI server due to an exception: {0}" ), e . Message )
2025-02-15 15:41:12 +01:00
};
2025-02-09 12:36:37 +01:00
}
}
public async Task < APIResponse < DataSourceInfo >> GetDataSourceInfoAsync ( CancellationToken cancellationToken = default )
{
2025-02-15 15:41:12 +01:00
try
{
using var request = new HttpRequestMessage ( HttpMethod . Get , "/dataSource" );
2026-04-16 16:48:47 +02:00
request . Headers . Add ( "token" , this . SecurityToken );
2025-02-15 15:41:12 +01:00
2026-04-16 16:48:47 +02:00
using var response = await this . HttpClient . SendAsync ( request , cancellationToken );
2025-02-15 15:41:12 +01:00
if (! response . IsSuccessStatusCode )
{
return new ()
{
Successful = false ,
2025-05-04 14:59:30 +02:00
Message = string . Format ( TB ( "Failed to retrieve the data source information: there was an issue communicating with the ERI server. Code: {0}, Reason: {1}" ), response . StatusCode , response . ReasonPhrase )
2025-02-15 15:41:12 +01:00
};
}
var dataSourceInfo = await response . Content . ReadFromJsonAsync < DataSourceInfo >( JSON_OPTIONS , cancellationToken );
if ( dataSourceInfo == default )
{
return new ()
{
Successful = false ,
2025-05-04 14:59:30 +02:00
Message = TB ( "Failed to retrieve the data source information: the ERI server did not return a valid response." )
2025-02-15 15:41:12 +01:00
};
}
2025-02-09 12:36:37 +01:00
2025-02-15 15:41:12 +01:00
return new ()
{
Successful = true ,
Data = dataSourceInfo
};
}
catch ( TaskCanceledException )
2025-02-09 12:36:37 +01:00
{
return new ()
{
Successful = false ,
2025-05-04 14:59:30 +02:00
Message = TB ( "Failed to retrieve the data source information: the request was canceled either by the user or due to a timeout." )
2025-02-09 12:36:37 +01:00
};
}
2025-02-15 15:41:12 +01:00
catch ( Exception e )
2025-02-09 12:36:37 +01:00
{
return new ()
{
Successful = false ,
2025-05-04 14:59:30 +02:00
Message = string . Format ( TB ( "Failed to retrieve the data source information due to an exception: {0}" ), e . Message )
2025-02-09 12:36:37 +01:00
};
}
}
public async Task < APIResponse < List < EmbeddingInfo >>> GetEmbeddingInfoAsync ( CancellationToken cancellationToken = default )
{
2025-02-15 15:41:12 +01:00
try
{
using var request = new HttpRequestMessage ( HttpMethod . Get , "/embedding/info" );
2026-04-16 16:48:47 +02:00
request . Headers . Add ( "token" , this . SecurityToken );
2025-02-15 15:41:12 +01:00
2026-04-16 16:48:47 +02:00
using var response = await this . HttpClient . SendAsync ( request , cancellationToken );
2025-02-15 15:41:12 +01:00
if (! response . IsSuccessStatusCode )
{
return new ()
{
Successful = false ,
2025-05-04 14:59:30 +02:00
Message = string . Format ( TB ( "Failed to retrieve the embedding information: there was an issue communicating with the ERI server. Code: {0}, Reason: {1}" ), response . StatusCode , response . ReasonPhrase )
2025-02-15 15:41:12 +01:00
};
}
2025-02-09 12:36:37 +01:00
2025-02-15 15:41:12 +01:00
var embeddingInfo = await response . Content . ReadFromJsonAsync < List < EmbeddingInfo >>( JSON_OPTIONS , cancellationToken );
if ( embeddingInfo is null )
{
return new ()
{
Successful = false ,
2025-05-04 14:59:30 +02:00
Message = TB ( "Failed to retrieve the embedding information: the ERI server did not return a valid response." )
2025-02-15 15:41:12 +01:00
};
}
return new ()
{
Successful = true ,
Data = embeddingInfo
};
}
catch ( TaskCanceledException )
2025-02-09 12:36:37 +01:00
{
return new ()
{
Successful = false ,
2025-05-04 14:59:30 +02:00
Message = TB ( "Failed to retrieve the embedding information: the request was canceled either by the user or due to a timeout." )
2025-02-09 12:36:37 +01:00
};
}
2025-02-15 15:41:12 +01:00
catch ( Exception e )
2025-02-09 12:36:37 +01:00
{
return new ()
{
Successful = false ,
2025-05-04 14:59:30 +02:00
Message = string . Format ( TB ( "Failed to retrieve the embedding information due to an exception: {0}" ), e . Message )
2025-02-09 12:36:37 +01:00
};
}
}
public async Task < APIResponse < List < RetrievalInfo >>> GetRetrievalInfoAsync ( CancellationToken cancellationToken = default )
{
2025-02-15 15:41:12 +01:00
try
{
using var request = new HttpRequestMessage ( HttpMethod . Get , "/retrieval/info" );
2026-04-16 16:48:47 +02:00
request . Headers . Add ( "token" , this . SecurityToken );
2025-02-15 15:41:12 +01:00
2026-04-16 16:48:47 +02:00
using var response = await this . HttpClient . SendAsync ( request , cancellationToken );
2025-02-15 15:41:12 +01:00
if (! response . IsSuccessStatusCode )
{
return new ()
{
Successful = false ,
2025-05-04 14:59:30 +02:00
Message = string . Format ( TB ( "Failed to retrieve the retrieval information: there was an issue communicating with the ERI server. Code: {0}, Reason: {1}" ), response . StatusCode , response . ReasonPhrase )
2025-02-15 15:41:12 +01:00
};
}
var retrievalInfo = await response . Content . ReadFromJsonAsync < List < RetrievalInfo >>( JSON_OPTIONS , cancellationToken );
if ( retrievalInfo is null )
{
return new ()
{
Successful = false ,
2025-05-04 14:59:30 +02:00
Message = TB ( "Failed to retrieve the retrieval information: the ERI server did not return a valid response." )
2025-02-15 15:41:12 +01:00
};
}
2025-02-09 12:36:37 +01:00
2025-02-15 15:41:12 +01:00
return new ()
{
Successful = true ,
Data = retrievalInfo
};
}
catch ( TaskCanceledException )
2025-02-09 12:36:37 +01:00
{
return new ()
{
Successful = false ,
2025-05-04 14:59:30 +02:00
Message = TB ( "Failed to retrieve the retrieval information: the request was canceled either by the user or due to a timeout." )
2025-02-09 12:36:37 +01:00
};
}
2025-02-15 15:41:12 +01:00
catch ( Exception e )
2025-02-09 12:36:37 +01:00
{
return new ()
{
Successful = false ,
2025-05-04 14:59:30 +02:00
Message = string . Format ( TB ( "Failed to retrieve the retrieval information due to an exception: {0}" ), e . Message )
2025-02-09 12:36:37 +01:00
};
}
}
public async Task < APIResponse < List < Context >>> ExecuteRetrievalAsync ( RetrievalRequest request , CancellationToken cancellationToken = default )
{
2025-02-15 15:41:12 +01:00
try
{
using var requestMessage = new HttpRequestMessage ( HttpMethod . Post , "/retrieval" );
2026-04-16 16:48:47 +02:00
requestMessage . Headers . Add ( "token" , this . SecurityToken );
2025-02-09 12:36:37 +01:00
2025-02-15 15:41:12 +01:00
using var content = new StringContent ( JsonSerializer . Serialize ( request , JSON_OPTIONS ), Encoding . UTF8 , "application/json" );
requestMessage . Content = content ;
2026-04-16 16:48:47 +02:00
using var response = await this . HttpClient . SendAsync ( requestMessage , cancellationToken );
2025-02-15 15:41:12 +01:00
if (! response . IsSuccessStatusCode )
{
return new ()
{
Successful = false ,
2025-05-04 14:59:30 +02:00
Message = string . Format ( TB ( "Failed to execute the retrieval request: there was an issue communicating with the ERI server. Code: {0}, Reason: {1}" ), response . StatusCode , response . ReasonPhrase )
2025-02-15 15:41:12 +01:00
};
}
var contexts = await response . Content . ReadFromJsonAsync < List < Context >>( JSON_OPTIONS , cancellationToken );
if ( contexts is null )
{
return new ()
{
Successful = false ,
2025-05-04 14:59:30 +02:00
Message = TB ( "Failed to execute the retrieval request: the ERI server did not return a valid response." )
2025-02-15 15:41:12 +01:00
};
}
2025-02-09 12:36:37 +01:00
2025-02-15 15:41:12 +01:00
return new ()
{
Successful = true ,
Data = contexts
};
}
catch ( TaskCanceledException )
2025-02-09 12:36:37 +01:00
{
return new ()
{
Successful = false ,
2025-05-04 14:59:30 +02:00
Message = TB ( "Failed to execute the retrieval request: the request was canceled either by the user or due to a timeout." )
2025-02-09 12:36:37 +01:00
};
}
2025-02-15 15:41:12 +01:00
catch ( Exception e )
2025-02-09 12:36:37 +01:00
{
return new ()
{
Successful = false ,
2025-05-04 14:59:30 +02:00
Message = string . Format ( TB ( "Failed to execute the retrieval request due to an exception: {0}" ), e . Message )
2025-02-09 12:36:37 +01:00
};
}
}
public async Task < APIResponse < SecurityRequirements >> GetSecurityRequirementsAsync ( CancellationToken cancellationToken = default )
{
2025-02-15 15:41:12 +01:00
try
{
using var request = new HttpRequestMessage ( HttpMethod . Get , "/security/requirements" );
2026-04-16 16:48:47 +02:00
request . Headers . Add ( "token" , this . SecurityToken );
2025-02-15 15:41:12 +01:00
2026-04-16 16:48:47 +02:00
using var response = await this . HttpClient . SendAsync ( request , cancellationToken );
2025-02-15 15:41:12 +01:00
if (! response . IsSuccessStatusCode )
{
return new ()
{
Successful = false ,
2025-05-04 14:59:30 +02:00
Message = string . Format ( TB ( "Failed to retrieve the security requirements: there was an issue communicating with the ERI server. Code: {0}, Reason: {1}" ), response . StatusCode , response . ReasonPhrase )
2025-02-15 15:41:12 +01:00
};
}
2025-02-09 12:36:37 +01:00
2025-02-15 15:41:12 +01:00
var securityRequirements = await response . Content . ReadFromJsonAsync < SecurityRequirements >( JSON_OPTIONS , cancellationToken );
if ( securityRequirements == default )
{
return new ()
{
Successful = false ,
2025-05-04 14:59:30 +02:00
Message = TB ( "Failed to retrieve the security requirements: the ERI server did not return a valid response." )
2025-02-15 15:41:12 +01:00
};
}
return new ()
{
Successful = true ,
Data = securityRequirements
};
}
catch ( TaskCanceledException )
2025-02-09 12:36:37 +01:00
{
return new ()
{
Successful = false ,
2025-05-04 14:59:30 +02:00
Message = TB ( "Failed to retrieve the security requirements: the request was canceled either by the user or due to a timeout." )
2025-02-09 12:36:37 +01:00
};
}
2025-02-15 15:41:12 +01:00
catch ( Exception e )
2025-02-09 12:36:37 +01:00
{
return new ()
{
Successful = false ,
2025-05-04 14:59:30 +02:00
Message = string . Format ( TB ( "Failed to retrieve the security requirements due to an exception: {0}" ), e . Message )
2025-02-09 12:36:37 +01:00
};
}
}
#endregion
}