diff --git a/cookbook/helper/ai_helper.py b/cookbook/helper/ai_helper.py index b614faa5c..e8cbde3d4 100644 --- a/cookbook/helper/ai_helper.py +++ b/cookbook/helper/ai_helper.py @@ -1,5 +1,6 @@ from django.utils import timezone from django.db.models import Sum +from litellm import CustomLogger from cookbook.models import AiLog @@ -22,4 +23,60 @@ def has_monthly_token(space): def can_perform_ai_request(space): - return has_monthly_token(space) and space.ai_enabled \ No newline at end of file + return (has_monthly_token(space) or space.ai_credits_balance > 0) and space.ai_enabled + + +class AiCallbackHandler(CustomLogger): + space = None + user = None + ai_provider = None + + def __init__(self, space, user, ai_provider): + super().__init__() + self.space = space + self.user = user + self.ai_provider = ai_provider + + def log_pre_api_call(self, model, messages, kwargs): + pass + + def log_post_api_call(self, kwargs, response_obj, start_time, end_time): + pass + + def log_success_event(self, kwargs, response_obj, start_time, end_time): + self.create_ai_log(kwargs, response_obj, start_time, end_time) + + def log_failure_event(self, kwargs, response_obj, start_time, end_time): + self.create_ai_log(kwargs, response_obj, start_time, end_time) + + def create_ai_log(self, kwargs, response_obj, start_time, end_time): + credit_cost = 0 + credits_from_balance = False + if self.ai_provider.log_credit_cost: + credit_cost = kwargs.get("response_cost", 0) * 100 + + print(not has_monthly_token(self.space) , self.space.ai_credits_balance > 0, not has_monthly_token(self.space) and self.space.ai_credits_balance > 0) + if (not has_monthly_token(self.space)) and self.space.ai_credits_balance > 0: + print('taking credits from balance') + self.space.ai_credits_balance = max(0, self.space.ai_credits_balance - credit_cost) + print('setting from balance to true') + credits_from_balance = True + print('saving space') + self.space.save() + print('done') + else: + print('not taking credits from balance') + + print('creating AI log with credit cost ', credit_cost , ' from balance: ', credits_from_balance) + AiLog.objects.create( + created_by=self.user, + space=self.space, + ai_provider=self.ai_provider, + start_time=start_time, + end_time=end_time, + input_tokens=response_obj['usage']['prompt_tokens'], + output_tokens=response_obj['usage']['completion_tokens'], + function=AiLog.F_FILE_IMPORT, + credit_cost=credit_cost, + credits_from_balance=credits_from_balance, + ) diff --git a/cookbook/migrations/0227_space_ai_default_provider_and_more.py b/cookbook/migrations/0227_space_ai_default_provider_and_more.py new file mode 100644 index 000000000..fc3d12254 --- /dev/null +++ b/cookbook/migrations/0227_space_ai_default_provider_and_more.py @@ -0,0 +1,24 @@ +# Generated by Django 4.2.22 on 2025-09-09 11:40 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('cookbook', '0226_aiprovider_log_credit_cost_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='space', + name='ai_default_provider', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='space_ai_default_provider', to='cookbook.aiprovider'), + ), + migrations.AlterField( + model_name='space', + name='ai_credits_balance', + field=models.DecimalField(decimal_places=4, default=0, max_digits=16), + ), + ] diff --git a/cookbook/models.py b/cookbook/models.py index 4b870a844..42ada30f4 100644 --- a/cookbook/models.py +++ b/cookbook/models.py @@ -331,7 +331,8 @@ class Space(ExportModelOperationsMixin('space'), models.Model): ai_enabled = models.BooleanField(default=True) ai_credits_monthly = models.IntegerField(default=100) - ai_credits_balance = models.IntegerField(default=0) + ai_credits_balance = models.DecimalField(default=0, max_digits=16, decimal_places=4) + ai_default_provider = models.ForeignKey("AiProvider", on_delete=models.SET_NULL, null=True, blank=True, related_name='space_ai_default_provider') internal_note = models.TextField(blank=True, null=True) diff --git a/cookbook/serializer.py b/cookbook/serializer.py index 70cce2a21..c3b2ef8c7 100644 --- a/cookbook/serializer.py +++ b/cookbook/serializer.py @@ -326,83 +326,6 @@ class UserFileViewSerializer(serializers.ModelSerializer): read_only_fields = ('id', 'file', 'file_download', 'file_size_kb', 'preview', 'created_by', 'created_at') -class SpaceSerializer(WritableNestedModelSerializer): - created_by = UserSerializer(read_only=True) - user_count = serializers.SerializerMethodField('get_user_count') - recipe_count = serializers.SerializerMethodField('get_recipe_count') - file_size_mb = serializers.SerializerMethodField('get_file_size_mb') - ai_monthly_credits_used = serializers.SerializerMethodField('get_ai_monthly_credits_used') - food_inherit = FoodInheritFieldSerializer(many=True) - image = UserFileViewSerializer(required=False, many=False, allow_null=True) - nav_logo = UserFileViewSerializer(required=False, many=False, allow_null=True) - custom_space_theme = UserFileViewSerializer(required=False, many=False, allow_null=True) - logo_color_32 = UserFileViewSerializer(required=False, many=False, allow_null=True) - logo_color_128 = UserFileViewSerializer(required=False, many=False, allow_null=True) - logo_color_144 = UserFileViewSerializer(required=False, many=False, allow_null=True) - logo_color_180 = UserFileViewSerializer(required=False, many=False, allow_null=True) - logo_color_192 = UserFileViewSerializer(required=False, many=False, allow_null=True) - logo_color_512 = UserFileViewSerializer(required=False, many=False, allow_null=True) - logo_color_svg = UserFileViewSerializer(required=False, many=False, allow_null=True) - - @extend_schema_field(int) - def get_user_count(self, obj): - return UserSpace.objects.filter(space=obj).count() - - @extend_schema_field(int) - def get_recipe_count(self, obj): - return Recipe.objects.filter(space=obj).count() - - @extend_schema_field(int) - def get_ai_monthly_credits_used(self, obj): - return get_monthly_token_usage(obj) - - @extend_schema_field(float) - def get_file_size_mb(self, obj): - try: - return UserFile.objects.filter(space=obj).aggregate(Sum('file_size_kb'))['file_size_kb__sum'] / 1000 - except TypeError: - return 0 - - def create(self, validated_data): - raise ValidationError('Cannot create using this endpoint') - - class Meta: - model = Space - fields = ( - 'id', 'name', 'created_by', 'created_at', 'message', 'max_recipes', 'max_file_storage_mb', 'max_users', - 'allow_sharing', 'demo', 'food_inherit', 'user_count', 'recipe_count', 'file_size_mb', - 'image', 'nav_logo', 'space_theme', 'custom_space_theme', 'nav_bg_color', 'nav_text_color', - 'logo_color_32', 'logo_color_128', 'logo_color_144', 'logo_color_180', 'logo_color_192', 'logo_color_512', 'logo_color_svg', 'ai_credits_monthly', - 'ai_credits_balance', 'ai_monthly_credits_used', 'ai_enabled') - read_only_fields = ( - 'id', 'created_by', 'created_at', 'max_recipes', 'max_file_storage_mb', 'max_users', 'allow_sharing', - 'demo', 'ai_credits_monthly', 'ai_credits_balance', 'ai_monthly_credits_used') - - -class UserSpaceSerializer(WritableNestedModelSerializer): - user = UserSerializer(read_only=True) - groups = GroupSerializer(many=True) - - def validate(self, data): - if self.instance.user == self.context['request'].space.created_by: # can't change space owner permission - raise serializers.ValidationError(_('Cannot modify Space owner permission.')) - return super().validate(data) - - def create(self, validated_data): - raise ValidationError('Cannot create using this endpoint') - - class Meta: - model = UserSpace - fields = ('id', 'user', 'space', 'groups', 'active', 'internal_note', 'invite_link', 'created_at', 'updated_at',) - read_only_fields = ('id', 'invite_link', 'created_at', 'updated_at', 'space') - - -class SpacedModelSerializer(serializers.ModelSerializer): - def create(self, validated_data): - validated_data['space'] = self.context['request'].space - return super().create(validated_data) - - class AiProviderSerializer(serializers.ModelSerializer): api_key = serializers.CharField(required=False, write_only=True) @@ -442,6 +365,96 @@ class AiLogSerializer(serializers.ModelSerializer): read_only_fields = ('__all__',) +class SpaceSerializer(WritableNestedModelSerializer): + created_by = UserSerializer(read_only=True) + user_count = serializers.SerializerMethodField('get_user_count') + recipe_count = serializers.SerializerMethodField('get_recipe_count') + file_size_mb = serializers.SerializerMethodField('get_file_size_mb') + ai_monthly_credits_used = serializers.SerializerMethodField('get_ai_monthly_credits_used') + ai_default_provider = AiProviderSerializer(required=False, allow_null=True) + food_inherit = FoodInheritFieldSerializer(many=True) + image = UserFileViewSerializer(required=False, many=False, allow_null=True) + nav_logo = UserFileViewSerializer(required=False, many=False, allow_null=True) + custom_space_theme = UserFileViewSerializer(required=False, many=False, allow_null=True) + logo_color_32 = UserFileViewSerializer(required=False, many=False, allow_null=True) + logo_color_128 = UserFileViewSerializer(required=False, many=False, allow_null=True) + logo_color_144 = UserFileViewSerializer(required=False, many=False, allow_null=True) + logo_color_180 = UserFileViewSerializer(required=False, many=False, allow_null=True) + logo_color_192 = UserFileViewSerializer(required=False, many=False, allow_null=True) + logo_color_512 = UserFileViewSerializer(required=False, many=False, allow_null=True) + logo_color_svg = UserFileViewSerializer(required=False, many=False, allow_null=True) + + @extend_schema_field(int) + def get_user_count(self, obj): + return UserSpace.objects.filter(space=obj).count() + + @extend_schema_field(int) + def get_recipe_count(self, obj): + return Recipe.objects.filter(space=obj).count() + + @extend_schema_field(int) + def get_ai_monthly_credits_used(self, obj): + return get_monthly_token_usage(obj) + + @extend_schema_field(float) + def get_file_size_mb(self, obj): + try: + return UserFile.objects.filter(space=obj).aggregate(Sum('file_size_kb'))['file_size_kb__sum'] / 1000 + except TypeError: + return 0 + + def create(self, validated_data): + raise ValidationError('Cannot create using this endpoint') + + def update(self, instance, validated_data): + if 'ai_enabled' in validated_data and not self.context['request'].user.is_superuser: + del validated_data['ai_enabled'] + + if 'ai_credits_monthly' in validated_data and not self.context['request'].user.is_superuser: + del validated_data['ai_credits_monthly'] + + if 'ai_credits_balance' in validated_data and not self.context['request'].user.is_superuser: + del validated_data['ai_credits_balance'] + + return super().update(instance, validated_data) + + class Meta: + model = Space + fields = ( + 'id', 'name', 'created_by', 'created_at', 'message', 'max_recipes', 'max_file_storage_mb', 'max_users', + 'allow_sharing', 'demo', 'food_inherit', 'user_count', 'recipe_count', 'file_size_mb', + 'image', 'nav_logo', 'space_theme', 'custom_space_theme', 'nav_bg_color', 'nav_text_color', + 'logo_color_32', 'logo_color_128', 'logo_color_144', 'logo_color_180', 'logo_color_192', 'logo_color_512', 'logo_color_svg', 'ai_credits_monthly', + 'ai_credits_balance', 'ai_monthly_credits_used', 'ai_enabled', 'ai_default_provider') + read_only_fields = ( + 'id', 'created_by', 'created_at', 'max_recipes', 'max_file_storage_mb', 'max_users', 'allow_sharing', + 'demo', 'ai_monthly_credits_used') + + +class UserSpaceSerializer(WritableNestedModelSerializer): + user = UserSerializer(read_only=True) + groups = GroupSerializer(many=True) + + def validate(self, data): + if self.instance.user == self.context['request'].space.created_by: # can't change space owner permission + raise serializers.ValidationError(_('Cannot modify Space owner permission.')) + return super().validate(data) + + def create(self, validated_data): + raise ValidationError('Cannot create using this endpoint') + + class Meta: + model = UserSpace + fields = ('id', 'user', 'space', 'groups', 'active', 'internal_note', 'invite_link', 'created_at', 'updated_at',) + read_only_fields = ('id', 'invite_link', 'created_at', 'updated_at', 'space') + + +class SpacedModelSerializer(serializers.ModelSerializer): + def create(self, validated_data): + validated_data['space'] = self.context['request'].space + return super().create(validated_data) + + class MealTypeSerializer(SpacedModelSerializer, WritableNestedModelSerializer): def create(self, validated_data): @@ -1610,7 +1623,6 @@ class ServerSettingsSerializer(serializers.Serializer): # TODO add all other relevant settings including path/url related ones? shopping_min_autosync_interval = serializers.CharField() enable_pdf_export = serializers.BooleanField() - enable_ai_import = serializers.BooleanField() disable_external_connectors = serializers.BooleanField() terms_url = serializers.CharField() privacy_url = serializers.CharField() diff --git a/cookbook/views/api.py b/cookbook/views/api.py index d1a785e4b..e1cf43565 100644 --- a/cookbook/views/api.py +++ b/cookbook/views/api.py @@ -65,7 +65,7 @@ from cookbook.connectors.connector_manager import ConnectorManager, ActionType from cookbook.forms import ImportForm, ImportExportBase from cookbook.helper import recipe_url_import as helper from cookbook.helper.HelperFunctions import str2bool, validate_import_url -from cookbook.helper.ai_helper import has_monthly_token, can_perform_ai_request +from cookbook.helper.ai_helper import has_monthly_token, can_perform_ai_request, AiCallbackHandler from cookbook.helper.image_processing import handle_image from cookbook.helper.ingredient_parser import IngredientParser from cookbook.helper.open_data_importer import OpenDataImporter @@ -117,7 +117,7 @@ from cookbook.serializer import (AccessTokenSerializer, AutomationSerializer, Au from cookbook.version_info import TANDOOR_VERSION from cookbook.views.import_export import get_integration from recipes import settings -from recipes.settings import DRF_THROTTLE_RECIPE_URL_IMPORT, FDC_API_KEY, AI_RATELIMIT, AI_API_KEY, AI_MODEL_NAME +from recipes.settings import DRF_THROTTLE_RECIPE_URL_IMPORT, FDC_API_KEY, AI_RATELIMIT DateExample = OpenApiExample('Date Format', value='1972-12-05', request_only=True) BeforeDateExample = OpenApiExample('Before Date Format', value='-1972-12-05', request_only=True) @@ -2041,25 +2041,7 @@ class AiImportView(APIView): ai_provider = AiProvider.objects.filter(pk=serializer.validated_data['ai_provider_id']).filter(Q(space=request.space) | Q(space__isnull=True)).first() - def log_ai_request(kwargs, completion_response, start_time, end_time): - credit_cost = 0 - if ai_provider.log_credit_cost: - credit_cost = kwargs.get("response_cost", 0) * 100 - - AiLog.objects.create( - created_by=request.user, - space=request.space, - ai_provider=ai_provider, - start_time=start_time, - end_time=end_time, - input_tokens=completion_response['usage']['prompt_tokens'], - output_tokens=completion_response['usage']['completion_tokens'], - function=AiLog.F_FILE_IMPORT, - credit_cost=credit_cost, - credits_from_balance=False, # TODO implement - ) - - litellm.success_callback = [log_ai_request] + litellm.callbacks = [AiCallbackHandler(request.space, request.user, ai_provider)] messages = [] uploaded_file = serializer.validated_data['file'] @@ -2442,7 +2424,6 @@ class ServerSettingsViewSet(viewsets.GenericViewSet): # Attention: No login required, do not return sensitive data s['shopping_min_autosync_interval'] = settings.SHOPPING_MIN_AUTOSYNC_INTERVAL s['enable_pdf_export'] = settings.ENABLE_PDF_EXPORT - s['enable_ai_import'] = settings.AI_API_KEY != '' s['disable_external_connectors'] = settings.DISABLE_EXTERNAL_CONNECTORS s['terms_url'] = settings.TERMS_URL s['privacy_url'] = settings.PRIVACY_URL diff --git a/docs/system/configuration.md b/docs/system/configuration.md index b3a333bbf..f5615f688 100644 --- a/docs/system/configuration.md +++ b/docs/system/configuration.md @@ -484,6 +484,11 @@ Sets the monthly default credit limit for AI usage SPACE_AI_CREDITS_MONTHLY=100 ``` +Ratelimit for AI API +``` +AI_RATELIMIT=60/hour +``` + #### FDC Api The FDC Api is used to automatically load nutrition information from diff --git a/recipes/settings.py b/recipes/settings.py index ba9ad58f7..fadef2ace 100644 --- a/recipes/settings.py +++ b/recipes/settings.py @@ -139,8 +139,6 @@ HCAPTCHA_SECRET = os.getenv('HCAPTCHA_SECRET', '') FDC_API_KEY = os.getenv('FDC_API_KEY', 'DEMO_KEY') -AI_API_KEY = os.getenv('AI_API_KEY', '') -AI_MODEL_NAME = os.getenv('AI_MODEL_NAME', 'gemini/gemini-2.0-flash') AI_RATELIMIT = os.getenv('AI_RATELIMIT', '60/hour') SHARING_ABUSE = extract_bool('SHARING_ABUSE', False) diff --git a/vue3/src/components/display/HelpView.vue b/vue3/src/components/display/HelpView.vue index 6f0858485..333e51c1c 100644 --- a/vue3/src/components/display/HelpView.vue +++ b/vue3/src/components/display/HelpView.vue @@ -14,6 +14,7 @@ + @@ -105,6 +106,35 @@ {{ $t('Import') }} + + +

Tandoor has several functions that allow you to use AI to automatically perform certain tasks like importing recipes from a PDFs or images. +

+ +

+ To use AI you must first configure an AI Provider. This can also be done globally for all spaces by the person operating your Tandoor Server. +

+

+ Some AI Providers are available globally for every space to use. You can also configure additional AI Providers for your space only. +

+ +

+ To prevent accidental AI cost you can review your AI usage using the AI Log. The Server Administrator can also set AI usage limits for your space (either monthly or using a balance). +

+

+ Depending on your subscription you will have different AI Credits available for your space every month. Additionally you might have a Credit balance + that will be used once your monthly limit is reached. +

+ + + {{ $t('AiProvider') }} + + + {{ $t('AiLog') }} + + {{ $t('SpaceSettings') }} + {{ $t('Import') }} +

Units allow you to measure how much of something you need in a recipe or on a shopping list. diff --git a/vue3/src/components/display/RecipeView.vue b/vue3/src/components/display/RecipeView.vue index 440752daa..347536f94 100644 --- a/vue3/src/components/display/RecipeView.vue +++ b/vue3/src/components/display/RecipeView.vue @@ -223,7 +223,7 @@ const recipe = defineModel({required: true}) const servings = ref(1) const showFullRecipeName = ref(false) -const selectedAiProvider = ref(undefined) +const selectedAiProvider = ref(useUserPreferenceStore().activeSpace.aiDefaultProvider) /** * factor for multiplying ingredient amounts based on recipe base servings and user selected servings diff --git a/vue3/src/components/settings/SpaceSettings.vue b/vue3/src/components/settings/SpaceSettings.vue index 36a4216c2..80c55decd 100644 --- a/vue3/src/components/settings/SpaceSettings.vue +++ b/vue3/src/components/settings/SpaceSettings.vue @@ -78,19 +78,38 @@ + {{ $t('Save') }} - +

{{ $t('AI') }}

+ +

+ + {{ $t('AISettingsHostedHelp') }} + + + {{ $t('SettingsOnlySuperuser') }} + +

+ + + {{ $t('Save') }} -

{{$t('Cosmetic')}}

- {{$t('Space_Cosmetic_Settings')}} +

{{ $t('Cosmetic') }}

+ {{ $t('Space_Cosmetic_Settings') }} {{ $t('Nav_Color') }} - {{$t('Reset')}} + {{ $t('Reset') }} diff --git a/vue3/src/locales/ar.json b/vue3/src/locales/ar.json index 489d565c7..3d422fc95 100644 --- a/vue3/src/locales/ar.json +++ b/vue3/src/locales/ar.json @@ -1,4 +1,5 @@ { + "AISettingsHostedHelp": "", "API_Browser": "", "API_Documentation": "", "Add": "", @@ -14,6 +15,7 @@ "Added_by": "", "Added_on": "", "Advanced": "", + "AiCreditsBalance": "", "AiLog": "", "AiLogHelp": "", "AiModelHelp": "", @@ -109,6 +111,7 @@ "FoodOnHand": "", "Food_Alias": "", "Foods": "", + "FromBalance": "", "Fulltext": "", "FulltextHelp": "", "Fuzzy": "", @@ -176,6 +179,7 @@ "Message": "", "MissingProperties": "", "Month": "", + "MonthlyCredits": "", "MonthlyCreditsUsed": "", "Move": "", "MoveCategory": "", @@ -268,6 +272,7 @@ "Selected": "", "Servings": "", "Settings": "", + "SettingsOnlySuperuser": "", "Share": "", "Shopping_Categories": "", "Shopping_Category": "", diff --git a/vue3/src/locales/bg.json b/vue3/src/locales/bg.json index c04e4e7ea..69f324db5 100644 --- a/vue3/src/locales/bg.json +++ b/vue3/src/locales/bg.json @@ -1,4 +1,5 @@ { + "AISettingsHostedHelp": "", "API_Browser": "", "API_Documentation": "", "Add": "Добави", @@ -14,6 +15,7 @@ "Added_by": "Добавено от", "Added_on": "Добавено", "Advanced": "Разширено", + "AiCreditsBalance": "", "AiLog": "", "AiLogHelp": "", "AiModelHelp": "", @@ -106,6 +108,7 @@ "FoodOnHand": "Имате {храна} под ръка.", "Food_Alias": "Псевдоним на храната", "Foods": "Храни", + "FromBalance": "", "Fulltext": "", "FulltextHelp": "", "Fuzzy": "", @@ -170,6 +173,7 @@ "Merge_Keyword": "Обединяване на ключова дума", "MissingProperties": "", "Month": "Месец", + "MonthlyCredits": "", "MonthlyCreditsUsed": "", "Move": "Премести", "MoveCategory": "Премести към: ", @@ -261,6 +265,7 @@ "Selected": "Избрано", "Servings": "Порции", "Settings": "Настройки", + "SettingsOnlySuperuser": "", "Share": "Споделяне", "Shopping_Categories": "Категории за пазаруване", "Shopping_Category": "Категория за пазаруване", diff --git a/vue3/src/locales/ca.json b/vue3/src/locales/ca.json index 6819105a2..c6c611a2a 100644 --- a/vue3/src/locales/ca.json +++ b/vue3/src/locales/ca.json @@ -1,4 +1,5 @@ { + "AISettingsHostedHelp": "", "API": "API", "API_Browser": "", "API_Documentation": "", @@ -16,6 +17,7 @@ "Added_by": "Afegit per", "Added_on": "Afegit el", "Advanced": "Avançat", + "AiCreditsBalance": "", "AiLog": "", "AiLogHelp": "", "AiModelHelp": "", @@ -150,6 +152,7 @@ "Food_Alias": "Àlies per l'aliment", "Food_Replace": "Aliment equivalent", "Foods": "Aliments", + "FromBalance": "", "Fulltext": "", "FulltextHelp": "", "Fuzzy": "", @@ -227,6 +230,7 @@ "Message": "Missatge", "MissingProperties": "", "Month": "Mes", + "MonthlyCredits": "", "MonthlyCreditsUsed": "", "Move": "Moure", "MoveCategory": "Moure a: ", @@ -340,6 +344,7 @@ "Selected": "Seleccionat", "Servings": "Racions", "Settings": "Opcions", + "SettingsOnlySuperuser": "", "Share": "Compartir", "ShoppingBackgroundSyncWarning": "Error de la connexió, esperant per sincronitzar ...", "Shopping_Categories": "Categoria de compres", diff --git a/vue3/src/locales/cs.json b/vue3/src/locales/cs.json index 458d64fa8..a7f592202 100644 --- a/vue3/src/locales/cs.json +++ b/vue3/src/locales/cs.json @@ -1,4 +1,5 @@ { + "AISettingsHostedHelp": "", "API": "API", "API_Browser": "", "API_Documentation": "", @@ -16,6 +17,7 @@ "Added_by": "Přidáno uživatelem", "Added_on": "Přidáno v", "Advanced": "Rozšířené", + "AiCreditsBalance": "", "AiLog": "", "AiLogHelp": "", "AiModelHelp": "", @@ -149,6 +151,7 @@ "Food_Alias": "Přezdívka potraviny", "Food_Replace": "Nahrazení v potravině", "Foods": "Potraviny", + "FromBalance": "", "Fulltext": "", "FulltextHelp": "", "Fuzzy": "", @@ -225,6 +228,7 @@ "Message": "Zpráva", "MissingProperties": "", "Month": "Měsíc", + "MonthlyCredits": "", "MonthlyCreditsUsed": "", "Move": "Přesunout", "MoveCategory": "Přesunout do: ", @@ -337,6 +341,7 @@ "Selected": "Vybrané", "Servings": "Porce", "Settings": "Nastavení", + "SettingsOnlySuperuser": "", "Share": "Sdílet", "Shopping_Categories": "Kategorie nákupního seznamu", "Shopping_Category": "Kategorie nákupního seznamu", diff --git a/vue3/src/locales/da.json b/vue3/src/locales/da.json index 093c89053..49fcc8405 100644 --- a/vue3/src/locales/da.json +++ b/vue3/src/locales/da.json @@ -1,4 +1,5 @@ { + "AISettingsHostedHelp": "", "API": "API", "API_Browser": "", "API_Documentation": "", @@ -16,6 +17,7 @@ "Added_by": "Tilføjet af", "Added_on": "Tilføjet den", "Advanced": "Avanceret", + "AiCreditsBalance": "", "AiLog": "", "AiLogHelp": "", "AiModelHelp": "", @@ -150,6 +152,7 @@ "Food_Alias": "Alternativt navn til mad", "Food_Replace": "Erstat ingrediens", "Foods": "Mad", + "FromBalance": "", "Fulltext": "", "FulltextHelp": "", "Fuzzy": "", @@ -227,6 +230,7 @@ "Message": "Besked", "MissingProperties": "", "Month": "Måned", + "MonthlyCredits": "", "MonthlyCreditsUsed": "", "Move": "Flyt", "MoveCategory": "Flyt til: ", @@ -340,6 +344,7 @@ "Selected": "Valgt", "Servings": "Serveringer", "Settings": "Indstillinger", + "SettingsOnlySuperuser": "", "Share": "Del", "ShoppingBackgroundSyncWarning": "Dårligt netværk, afventer synkronisering ...", "Shopping_Categories": "Indkøbskategorier", diff --git a/vue3/src/locales/de.json b/vue3/src/locales/de.json index f07faa429..7b6230d8c 100644 --- a/vue3/src/locales/de.json +++ b/vue3/src/locales/de.json @@ -1,6 +1,7 @@ { "AI": "AI", "AIImportSubtitle": "Verwende AI um Fotos von Rezepten zu importieren.", + "AISettingsHostedHelp": "AI Verfügbarkeit und Credit Limits können über die Tarifverwaltung geändert werden. ", "API": "API", "APIKey": "API Schlüssel", "API_Browser": "API Browser", @@ -29,6 +30,7 @@ "Admin": "Admin", "Advanced": "Erweitert", "Advanced Search Settings": "Erweiterte Sucheinstellungen", + "AiCreditsBalance": "Credit Guthaben", "AiLog": "AI Protokoll", "AiLogHelp": "Eine Übersicht der AI Anfragen.", "AiModelHelp": "Die Liste enthält Modelle die offiziell Unterstützt und getestet wurden. Weitere modelle können manuell eingetragen werden.", @@ -212,6 +214,7 @@ "Food_Replace": "Essen Ersetzen", "Foods": "Lebensmittel", "Friday": "Freitag", + "FromBalance": "Guthaben verwendet", "Fulltext": "Volltext", "FulltextHelp": "Felder welche im Volltext durchsucht werden sollen. Tipp: Die Suchtypen 'web', 'raw' und 'phrase' funktionieren nur mit Volltext-Feldern.", "Fuzzy": "Unscharf", @@ -317,6 +320,7 @@ "ModelSelectResultsHelp": "Für mehr Ergebnisse suchen", "Monday": "Montag", "Month": "Monat", + "MonthlyCredits": "Monatliche Credits", "MonthlyCreditsUsed": "Monatliche Credits verwendet", "More": "Mehr", "Move": "Verschieben", @@ -471,6 +475,7 @@ "Servings": "Portionen", "ServingsText": "Portionstext", "Settings": "Einstellungen", + "SettingsOnlySuperuser": "Einige Einstellungen können nur vom Server Administrator verändert werden.", "Share": "Teilen", "ShopLater": "Später kaufen", "ShopNow": "Jetzt kaufen", diff --git a/vue3/src/locales/el.json b/vue3/src/locales/el.json index 06a9a9d8d..e028a56e1 100644 --- a/vue3/src/locales/el.json +++ b/vue3/src/locales/el.json @@ -1,4 +1,5 @@ { + "AISettingsHostedHelp": "", "API": "API", "API_Browser": "", "API_Documentation": "", @@ -16,6 +17,7 @@ "Added_by": "Προστέθηκε από", "Added_on": "Προστέθηκε στις", "Advanced": "Για προχωρημένους", + "AiCreditsBalance": "", "AiLog": "", "AiLogHelp": "", "AiModelHelp": "", @@ -150,6 +152,7 @@ "Food_Alias": "Ψευδώνυμο φαγητού", "Food_Replace": "Αντικατάσταση Φαγητού", "Foods": "Φαγητά", + "FromBalance": "", "Fulltext": "", "FulltextHelp": "", "Fuzzy": "", @@ -227,6 +230,7 @@ "Message": "Μήνυμα", "MissingProperties": "", "Month": "Μήνας", + "MonthlyCredits": "", "MonthlyCreditsUsed": "", "Move": "Μετακίνηση", "MoveCategory": "Μετακίνηση σε: ", @@ -340,6 +344,7 @@ "Selected": "Επιλεγμένο", "Servings": "Μερίδες", "Settings": "Ρυθμίσεις", + "SettingsOnlySuperuser": "", "Share": "Κοινοποίηση", "ShoppingBackgroundSyncWarning": "Κακό δίκτυο, αναμονή συγχρονισμού...", "Shopping_Categories": "Κατηγορίες αγορών", diff --git a/vue3/src/locales/en.json b/vue3/src/locales/en.json index 6bff74732..d2bcb085a 100644 --- a/vue3/src/locales/en.json +++ b/vue3/src/locales/en.json @@ -1,6 +1,7 @@ { "AI": "AI", "AIImportSubtitle": "Use AI to import images of recipes.", + "AISettingsHostedHelp": "You can enable AI features or change available credits by managing your subscription.", "API": "API", "APIKey": "API key", "API_Browser": "API Browser", @@ -27,6 +28,7 @@ "Added_on": "Added On", "Admin": "Admin", "Advanced": "Advanced", + "AiCreditsBalance": "Credit Balance", "AiLog": "AI Log", "AiLogHelp": "Overview of your spaces AI requests. ", "AiModelHelp": "The list contains model that are offically tested and supported. You can add additional models if you want.", @@ -210,6 +212,7 @@ "Food_Replace": "Food Replace", "Foods": "Foods", "Friday": "Friday", + "FromBalance": "From Balance", "Fulltext": "Fulltext", "FulltextHelp": "Fields to full text search. Note: 'web', 'phrase', and 'raw' search methods only function with fulltext fields.", "Fuzzy": "Fuzzy", @@ -315,6 +318,7 @@ "ModelSelectResultsHelp": "Search for more results", "Monday": "Monday", "Month": "Month", + "MonthlyCredits": "Monthly Credits", "MonthlyCreditsUsed": "Monthly credits used", "More": "More", "Move": "Move", @@ -469,6 +473,7 @@ "Servings": "Servings", "ServingsText": "Servings Text", "Settings": "Settings", + "SettingsOnlySuperuser": "Some Settings can only be changed by the Server Administrator.", "Share": "Share", "ShopLater": "Shop later", "ShopNow": "Shop now", diff --git a/vue3/src/locales/es.json b/vue3/src/locales/es.json index 9e9f0c149..b6eb754a4 100644 --- a/vue3/src/locales/es.json +++ b/vue3/src/locales/es.json @@ -1,6 +1,7 @@ { "AI": "IA", "AIImportSubtitle": "Usar IA para importar imágenes de recetas.", + "AISettingsHostedHelp": "", "API": "API", "API_Browser": "", "API_Documentation": "", @@ -26,6 +27,7 @@ "Added_on": "Añadido el", "Admin": "Administrador", "Advanced": "Avanzado", + "AiCreditsBalance": "", "AiLog": "", "AiLogHelp": "", "AiModelHelp": "", @@ -207,6 +209,7 @@ "Food_Replace": "Sustituir Alimento", "Foods": "Alimentos", "Friday": "Viernes", + "FromBalance": "", "GettingStarted": "Primeros pasos", "Global": "", "GlobalHelp": "", @@ -305,6 +308,7 @@ "ModelSelectResultsHelp": "Buscar más resultados", "Monday": "Lunes", "Month": "Mes", + "MonthlyCredits": "", "MonthlyCreditsUsed": "", "More": "Más", "Move": "Mover", @@ -453,6 +457,7 @@ "Servings": "Raciones", "ServingsText": "Texto de la porción", "Settings": "Opciones", + "SettingsOnlySuperuser": "", "Share": "Compartir", "ShopLater": "Comprar después", "ShopNow": "Comprar ahora", diff --git a/vue3/src/locales/fi.json b/vue3/src/locales/fi.json index 33996af11..47f674c28 100644 --- a/vue3/src/locales/fi.json +++ b/vue3/src/locales/fi.json @@ -1,4 +1,5 @@ { + "AISettingsHostedHelp": "", "API": "API", "API_Browser": "", "API_Documentation": "", @@ -17,6 +18,7 @@ "Added_on": "Lisätty", "Advanced": "Edistynyt", "Advanced Search Settings": "Tarkennetun Haun Asetukset", + "AiCreditsBalance": "", "AiLog": "", "AiLogHelp": "", "AiModelHelp": "", @@ -147,6 +149,7 @@ "Food_Alias": "Ruoan nimimerkki", "Food_Replace": "Korvaa Ruoka", "Foods": "Ruuat", + "FromBalance": "", "Fulltext": "", "FulltextHelp": "", "Fuzzy": "", @@ -221,6 +224,7 @@ "Message": "Viesti", "MissingProperties": "", "Month": "Kuukausi", + "MonthlyCredits": "", "MonthlyCreditsUsed": "", "Move": "Siirry", "MoveCategory": "Siirrä paikkaan: ", @@ -329,6 +333,7 @@ "Selected": "Valittu", "Servings": "Annokset", "Settings": "Asetukset", + "SettingsOnlySuperuser": "", "Share": "Jaa", "ShoppingBackgroundSyncWarning": "Huono verkkoyhteys, odotetaan synkronointia ...", "Shopping_Categories": "Ostoskategoriat", diff --git a/vue3/src/locales/fr.json b/vue3/src/locales/fr.json index bdd4f6fb9..b98c94b6e 100644 --- a/vue3/src/locales/fr.json +++ b/vue3/src/locales/fr.json @@ -1,6 +1,7 @@ { "AI": "IA", "AIImportSubtitle": "Utiliser l'IA pour importer des images de recettes.", + "AISettingsHostedHelp": "", "API": "API", "API_Browser": "", "API_Documentation": "", @@ -27,6 +28,7 @@ "Admin": "Admin", "Advanced": "Avancé", "Advanced Search Settings": "Paramètres de recherche avancée", + "AiCreditsBalance": "", "AiLog": "", "AiLogHelp": "", "AiModelHelp": "", @@ -210,6 +212,7 @@ "Food_Replace": "Remplacer l'aliment", "Foods": "Aliments", "Friday": "Vendredi", + "FromBalance": "", "Fulltext": "Texte intégral", "FulltextHelp": "Champs de recherche en texte intégral. Remarque : les méthodes de recherche \"web\", \"phrase\" et \"raw\" ne fonctionnent qu'avec des champs en texte intégral.", "Fuzzy": "Approximatif", @@ -312,6 +315,7 @@ "ModelSelectResultsHelp": "Chercher plus de résultats", "Monday": "Lundi", "Month": "Mois", + "MonthlyCredits": "", "MonthlyCreditsUsed": "", "More": "Plus", "Move": "Déplacer", @@ -466,6 +470,7 @@ "Servings": "Portions", "ServingsText": "Texte des portions", "Settings": "Paramètres", + "SettingsOnlySuperuser": "", "Share": "Partager", "ShopLater": "Acheter plus tard", "ShopNow": "Acheter maintenant", diff --git a/vue3/src/locales/he.json b/vue3/src/locales/he.json index c565a10d1..4bb95ce4e 100644 --- a/vue3/src/locales/he.json +++ b/vue3/src/locales/he.json @@ -1,4 +1,5 @@ { + "AISettingsHostedHelp": "", "API": "API", "API_Browser": "", "API_Documentation": "", @@ -16,6 +17,7 @@ "Added_by": "נוסף ע\"י", "Added_on": "נוסף ב", "Advanced": "מתקדם", + "AiCreditsBalance": "", "AiLog": "", "AiLogHelp": "", "AiModelHelp": "", @@ -150,6 +152,7 @@ "Food_Alias": "שם כינוי לאוכל", "Food_Replace": "החלף אוכל", "Foods": "מאכלים", + "FromBalance": "", "Fulltext": "", "FulltextHelp": "", "Fuzzy": "", @@ -227,6 +230,7 @@ "Message": "הודעה", "MissingProperties": "", "Month": "חודש", + "MonthlyCredits": "", "MonthlyCreditsUsed": "", "Move": "העברה", "MoveCategory": "העבר אל: ", @@ -340,6 +344,7 @@ "Selected": "נבחר", "Servings": "מנות", "Settings": "הגדרות", + "SettingsOnlySuperuser": "", "Share": "שיתוף", "ShoppingBackgroundSyncWarning": "בעיית תקשורת, מחכה לסנכון...", "Shopping_Categories": "קטגוריות קניות", diff --git a/vue3/src/locales/hr.json b/vue3/src/locales/hr.json index 0f2be32b3..1a34d4aeb 100644 --- a/vue3/src/locales/hr.json +++ b/vue3/src/locales/hr.json @@ -1,4 +1,5 @@ { + "AISettingsHostedHelp": "", "API": "API", "API_Browser": "", "API_Documentation": "", @@ -16,6 +17,7 @@ "Added_by": "Dodao", "Added_on": "Dodano", "Advanced": "Napredno", + "AiCreditsBalance": "", "AiLog": "", "AiLogHelp": "", "AiModelHelp": "", @@ -150,6 +152,7 @@ "Food_Alias": "Nadimci namirnice", "Food_Replace": "Zamjena namirnica", "Foods": "Namirnice", + "FromBalance": "", "Fulltext": "", "FulltextHelp": "", "Fuzzy": "", @@ -227,6 +230,7 @@ "Message": "Poruka", "MissingProperties": "", "Month": "Mjesec", + "MonthlyCredits": "", "MonthlyCreditsUsed": "", "Move": "Premjesti", "MoveCategory": "Premjesti u: ", @@ -340,6 +344,7 @@ "Selected": "Odabrano", "Servings": "Porcije", "Settings": "Postavke", + "SettingsOnlySuperuser": "", "Share": "Podijeli", "ShoppingBackgroundSyncWarning": "Loša mreža, čeka se sinkronizacija...", "Shopping_Categories": "Kategorije Kupovine", diff --git a/vue3/src/locales/hu.json b/vue3/src/locales/hu.json index ad24b17dd..950c715cd 100644 --- a/vue3/src/locales/hu.json +++ b/vue3/src/locales/hu.json @@ -1,4 +1,5 @@ { + "AISettingsHostedHelp": "", "API": "API", "API_Browser": "", "API_Documentation": "", @@ -16,6 +17,7 @@ "Added_by": "Hozzádta", "Added_on": "Hozzáadva", "Advanced": "Haladó", + "AiCreditsBalance": "", "AiLog": "", "AiLogHelp": "", "AiModelHelp": "", @@ -133,6 +135,7 @@ "Food_Alias": "", "Food_Replace": "Étel cseréje", "Foods": "Alapanyagok", + "FromBalance": "", "Fulltext": "", "FulltextHelp": "", "Fuzzy": "", @@ -208,6 +211,7 @@ "Message": "Üzenet", "MissingProperties": "", "Month": "Hónap", + "MonthlyCredits": "", "MonthlyCreditsUsed": "", "Move": "Mozgatás", "MoveCategory": "Áthelyezés ide: ", @@ -313,6 +317,7 @@ "Selected": "Kiválasztott", "Servings": "Adag", "Settings": "Beállítások", + "SettingsOnlySuperuser": "", "Share": "Megosztás", "Shopping_Categories": "Vásárlási kategóriák", "Shopping_Category": "Vásárlási kategória", diff --git a/vue3/src/locales/hy.json b/vue3/src/locales/hy.json index 17905f5de..93a9e3583 100644 --- a/vue3/src/locales/hy.json +++ b/vue3/src/locales/hy.json @@ -1,4 +1,5 @@ { + "AISettingsHostedHelp": "", "API_Browser": "", "API_Documentation": "", "Add": "", @@ -8,6 +9,7 @@ "Add_to_Plan": "Ավելացնել պլանին", "Add_to_Shopping": "Ավելացնել գնումներին", "Advanced Search Settings": "Ընդլայնված փնտրման կարգավորումներ", + "AiCreditsBalance": "", "AiLog": "", "AiLogHelp": "", "AiModelHelp": "", @@ -58,6 +60,7 @@ "File": "", "Files": "", "Food": "Սննդամթերք", + "FromBalance": "", "Fulltext": "", "FulltextHelp": "", "Fuzzy": "", @@ -89,6 +92,7 @@ "MergeAutomateHelp": "", "Merge_Keyword": "Միացնել բանալի բառը", "MissingProperties": "", + "MonthlyCredits": "", "MonthlyCreditsUsed": "", "Move": "Տեղափոխել", "Move_Food": "Տեղափոխել սննդամթերքը", @@ -136,6 +140,7 @@ "Selected": "", "Servings": "", "Settings": "Կարգավորումներ", + "SettingsOnlySuperuser": "", "Share": "", "Shopping_Category": "Գնումների կատեգորիա", "Shopping_list": "Գնումների ցուցակ", diff --git a/vue3/src/locales/id.json b/vue3/src/locales/id.json index 86f220f19..b954b74b1 100644 --- a/vue3/src/locales/id.json +++ b/vue3/src/locales/id.json @@ -1,4 +1,5 @@ { + "AISettingsHostedHelp": "", "API": "", "API_Browser": "", "API_Documentation": "", @@ -16,6 +17,7 @@ "Added_by": "", "Added_on": "", "Advanced": "", + "AiCreditsBalance": "", "AiLog": "", "AiLogHelp": "", "AiModelHelp": "", @@ -121,6 +123,7 @@ "FoodOnHand": "", "Food_Alias": "", "Foods": "", + "FromBalance": "", "Fulltext": "", "FulltextHelp": "", "Fuzzy": "", @@ -193,6 +196,7 @@ "Message": "", "MissingProperties": "", "Month": "", + "MonthlyCredits": "", "MonthlyCreditsUsed": "", "Move": "Bergerak", "MoveCategory": "", @@ -289,6 +293,7 @@ "Selected": "Terpilih", "Servings": "Porsi", "Settings": "Pengaturan", + "SettingsOnlySuperuser": "", "Share": "Bagikan", "Shopping_Categories": "Kategori Belanja", "Shopping_Category": "Kategori Belanja", diff --git a/vue3/src/locales/is.json b/vue3/src/locales/is.json index f6cf1390c..7a231b6b8 100644 --- a/vue3/src/locales/is.json +++ b/vue3/src/locales/is.json @@ -1,4 +1,5 @@ { + "AISettingsHostedHelp": "", "API": "", "API_Browser": "", "API_Documentation": "", @@ -16,6 +17,7 @@ "Added_by": "", "Added_on": "", "Advanced": "", + "AiCreditsBalance": "", "AiLog": "", "AiLogHelp": "", "AiModelHelp": "", @@ -149,6 +151,7 @@ "Food_Alias": "", "Food_Replace": "", "Foods": "", + "FromBalance": "", "Fulltext": "", "FulltextHelp": "", "Fuzzy": "", @@ -226,6 +229,7 @@ "Message": "", "MissingProperties": "", "Month": "", + "MonthlyCredits": "", "MonthlyCreditsUsed": "", "Move": "", "MoveCategory": "", @@ -339,6 +343,7 @@ "Selected": "", "Servings": "", "Settings": "", + "SettingsOnlySuperuser": "", "Share": "", "ShoppingBackgroundSyncWarning": "", "Shopping_Categories": "", diff --git a/vue3/src/locales/it.json b/vue3/src/locales/it.json index 416c7e56c..7e3f57690 100644 --- a/vue3/src/locales/it.json +++ b/vue3/src/locales/it.json @@ -1,6 +1,7 @@ { "AI": "IA", "AIImportSubtitle": "Utilizza IA per importare le immagini delle ricette.", + "AISettingsHostedHelp": "", "API": "API", "API_Browser": "", "API_Documentation": "", @@ -27,6 +28,7 @@ "Admin": "Amministratore", "Advanced": "Avanzate", "Advanced Search Settings": "Impostazioni avanzate di ricerca", + "AiCreditsBalance": "", "AiLog": "", "AiLogHelp": "", "AiModelHelp": "", @@ -210,6 +212,7 @@ "Food_Replace": "Sostituisci alimento", "Foods": "Alimenti", "Friday": "Venerdì", + "FromBalance": "", "Fulltext": "Fulltext", "FulltextHelp": "Campi per la ricerca full text. Nota: i metodi di ricerca 'web', 'phrase', e 'raw' funzionano solo con i campi fulltext.", "Fuzzy": "Fuzzy", @@ -314,6 +317,7 @@ "ModelSelectResultsHelp": "Cerca altri risultati", "Monday": "Lunedì", "Month": "Mese", + "MonthlyCredits": "", "MonthlyCreditsUsed": "", "More": "Altro", "Move": "Sposta", @@ -468,6 +472,7 @@ "Servings": "Porzioni", "ServingsText": "Testo porzioni", "Settings": "Impostazioni", + "SettingsOnlySuperuser": "", "Share": "Condividi", "ShopLater": "Compra dopo", "ShopNow": "Compra subito", diff --git a/vue3/src/locales/lt.json b/vue3/src/locales/lt.json index 19fb30cdc..b41a11d68 100644 --- a/vue3/src/locales/lt.json +++ b/vue3/src/locales/lt.json @@ -1,4 +1,5 @@ { + "AISettingsHostedHelp": "", "API": "", "API_Browser": "", "API_Documentation": "", @@ -16,6 +17,7 @@ "Added_by": "", "Added_on": "", "Advanced": "", + "AiCreditsBalance": "", "AiLog": "", "AiLogHelp": "", "AiModelHelp": "", @@ -135,6 +137,7 @@ "Food_Alias": "", "Food_Replace": "", "Foods": "", + "FromBalance": "", "Fulltext": "", "FulltextHelp": "", "Fuzzy": "", @@ -210,6 +213,7 @@ "Message": "", "MissingProperties": "", "Month": "", + "MonthlyCredits": "", "MonthlyCreditsUsed": "", "Move": "", "MoveCategory": "", @@ -317,6 +321,7 @@ "Selected": "", "Servings": "", "Settings": "", + "SettingsOnlySuperuser": "", "Share": "", "Shopping_Categories": "", "Shopping_Category": "", diff --git a/vue3/src/locales/lv.json b/vue3/src/locales/lv.json index 2c3da36f8..f9067c460 100644 --- a/vue3/src/locales/lv.json +++ b/vue3/src/locales/lv.json @@ -1,4 +1,5 @@ { + "AISettingsHostedHelp": "", "API": "", "API_Browser": "", "API_Documentation": "", @@ -16,6 +17,7 @@ "Added_by": "", "Added_on": "", "Advanced": "", + "AiCreditsBalance": "", "AiLog": "", "AiLogHelp": "", "AiModelHelp": "", @@ -150,6 +152,7 @@ "Food_Alias": "", "Food_Replace": "", "Foods": "", + "FromBalance": "", "Fulltext": "", "FulltextHelp": "", "Fuzzy": "", @@ -227,6 +230,7 @@ "Message": "", "MissingProperties": "", "Month": "", + "MonthlyCredits": "", "MonthlyCreditsUsed": "", "Move": "", "MoveCategory": "", @@ -340,6 +344,7 @@ "Selected": "", "Servings": "", "Settings": "", + "SettingsOnlySuperuser": "", "Share": "", "ShoppingBackgroundSyncWarning": "", "Shopping_Categories": "", diff --git a/vue3/src/locales/nb_NO.json b/vue3/src/locales/nb_NO.json index cb14f5c64..47837cf43 100644 --- a/vue3/src/locales/nb_NO.json +++ b/vue3/src/locales/nb_NO.json @@ -1,4 +1,5 @@ { + "AISettingsHostedHelp": "", "API": "API", "API_Browser": "", "API_Documentation": "", @@ -16,6 +17,7 @@ "Added_by": "Lagt til av", "Added_on": "Lagt til", "Advanced": "Avansert", + "AiCreditsBalance": "", "AiLog": "", "AiLogHelp": "", "AiModelHelp": "", @@ -141,6 +143,7 @@ "FoodOnHand": "Du har {food} på lager.", "Food_Alias": "Matrett Alias", "Foods": "", + "FromBalance": "", "Fulltext": "", "FulltextHelp": "", "Fuzzy": "", @@ -217,6 +220,7 @@ "Message": "Melding", "MissingProperties": "", "Month": "Måned", + "MonthlyCredits": "", "MonthlyCreditsUsed": "", "Move": "Flytt", "MoveCategory": "Flytt til: ", @@ -324,6 +328,7 @@ "Selected": "Valgte", "Servings": "Porsjoner", "Settings": "Innstillinger", + "SettingsOnlySuperuser": "", "Share": "Del", "ShoppingBackgroundSyncWarning": "Dårlig nettverkstilkobling, venter på synkronisering...", "Shopping_Categories": "Butikk Kategorier", diff --git a/vue3/src/locales/nl.json b/vue3/src/locales/nl.json index 11d1f19cd..e0cb32167 100644 --- a/vue3/src/locales/nl.json +++ b/vue3/src/locales/nl.json @@ -1,6 +1,7 @@ { "AI": "AI", "AIImportSubtitle": "Gebruik Al om afbeeldingen van recepten te importeren.", + "AISettingsHostedHelp": "", "API": "API", "API_Browser": "", "API_Documentation": "", @@ -28,6 +29,7 @@ "Admin": "Beheer", "Advanced": "Geavanceerd", "Advanced Search Settings": "Geavanceerde zoekinstellingen", + "AiCreditsBalance": "", "AiLog": "", "AiLogHelp": "", "AiModelHelp": "", @@ -211,6 +213,7 @@ "Food_Replace": "Voedingsmiddelen vervangen", "Foods": "Voedingsmiddelen", "Friday": "Vrijdag", + "FromBalance": "", "Fulltext": "Volledige tekst", "FulltextHelp": "Velden voor volledige tekstzoekopdrachten. Opmerking: de zoekmethoden ‘web’, ‘zin’ en ‘ruw’ werken alleen met volledige tekstvelden.", "Fuzzy": "Fuzzy", @@ -315,6 +318,7 @@ "ModelSelectResultsHelp": "Zoek naar meer resultaten", "Monday": "Maandag", "Month": "Maand", + "MonthlyCredits": "", "MonthlyCreditsUsed": "", "More": "Meer", "Move": "Verplaats", @@ -469,6 +473,7 @@ "Servings": "Porties", "ServingsText": "Portie tekst", "Settings": "Instellingen", + "SettingsOnlySuperuser": "", "Share": "Deel", "ShopLater": "Later boodschappen doen", "ShopNow": "Nu boodschappen doen", diff --git a/vue3/src/locales/pl.json b/vue3/src/locales/pl.json index df842c3be..e150db670 100644 --- a/vue3/src/locales/pl.json +++ b/vue3/src/locales/pl.json @@ -1,4 +1,5 @@ { + "AISettingsHostedHelp": "", "API": "API", "API_Browser": "", "API_Documentation": "", @@ -25,6 +26,7 @@ "Admin": "Administator", "Advanced": "Zaawansowany", "Advanced Search Settings": "Ustawienia zaawansowanego wyszukiwania", + "AiCreditsBalance": "", "AiLog": "", "AiLogHelp": "", "AiModelHelp": "", @@ -176,6 +178,7 @@ "Food_Alias": "Alias żywności", "Food_Replace": "Zastąp produkt", "Foods": "Żywność", + "FromBalance": "", "Fulltext": "", "FulltextHelp": "", "Fuzzy": "", @@ -253,6 +256,7 @@ "Message": "Wiadomość", "MissingProperties": "", "Month": "Miesiąc", + "MonthlyCredits": "", "MonthlyCreditsUsed": "", "Move": "Przenieś", "MoveCategory": "Przenieś do: ", @@ -366,6 +370,7 @@ "Selected": "Wybrane", "Servings": "Porcje", "Settings": "Ustawienia", + "SettingsOnlySuperuser": "", "Share": "Udostępnij", "ShoppingBackgroundSyncWarning": "Słaba sieć, oczekiwanie na synchronizację...", "Shopping_Categories": "Kategorie zakupów", diff --git a/vue3/src/locales/pt.json b/vue3/src/locales/pt.json index b90508f44..4d00b0899 100644 --- a/vue3/src/locales/pt.json +++ b/vue3/src/locales/pt.json @@ -1,4 +1,5 @@ { + "AISettingsHostedHelp": "", "API_Browser": "", "API_Documentation": "", "Add": "Adicionar", @@ -14,6 +15,7 @@ "Added_by": "Adicionado por", "Added_on": "Adicionado a", "Advanced": "Avançado", + "AiCreditsBalance": "", "AiLog": "", "AiLogHelp": "", "AiModelHelp": "", @@ -121,6 +123,7 @@ "FoodOnHand": "Tem {food} disponível.", "Food_Alias": "Alcunha da comida", "Foods": "", + "FromBalance": "", "Fulltext": "", "FulltextHelp": "", "Fuzzy": "", @@ -182,6 +185,7 @@ "Merge_Keyword": "Unir palavra-chave", "MissingProperties": "", "Month": "Mês", + "MonthlyCredits": "", "MonthlyCreditsUsed": "", "Move": "Mover", "MoveCategory": "Mover para: ", @@ -279,6 +283,7 @@ "Selected": "Selecionado", "Servings": "Doses", "Settings": "Definições", + "SettingsOnlySuperuser": "", "Share": "Partilhar", "Shopping_Categories": "Categorias de Compras", "Shopping_Category": "Categoria de Compras", diff --git a/vue3/src/locales/pt_BR.json b/vue3/src/locales/pt_BR.json index bea72c7cd..9ec300e33 100644 --- a/vue3/src/locales/pt_BR.json +++ b/vue3/src/locales/pt_BR.json @@ -1,6 +1,7 @@ { "AI": "IA", "AIImportSubtitle": "Use IA para importar imagens das receitas.", + "AISettingsHostedHelp": "", "API": "API", "API_Browser": "", "API_Documentation": "", @@ -26,6 +27,7 @@ "Added_on": "Incluído Em", "Admin": "Administrador", "Advanced": "Avançado", + "AiCreditsBalance": "", "AiLog": "", "AiLogHelp": "", "AiModelHelp": "", @@ -209,6 +211,7 @@ "Food_Replace": "Substituir Alimento", "Foods": "Alimentos", "Friday": "Sexta-feira", + "FromBalance": "", "Fulltext": "Texto completo", "FulltextHelp": "Campos para pesquisa textual completa. Observação: os métodos de pesquisa 'web', 'phrase' e 'raw' só funcionam com campos de pesquisa textual completa.", "Fuzzy": "Fuzzy", @@ -307,6 +310,7 @@ "Message": "Mensagem", "MissingProperties": "", "Month": "Mês", + "MonthlyCredits": "", "MonthlyCreditsUsed": "", "Move": "Mover", "MoveCategory": "Mover Para: ", @@ -414,6 +418,7 @@ "Selected": "Selecionado", "Servings": "Porções", "Settings": "Configurações", + "SettingsOnlySuperuser": "", "Share": "Compartilhar", "ShoppingBackgroundSyncWarning": "Rede ruim, aguardando sincronização...", "Shopping_Categories": "Categorias de Mercado", diff --git a/vue3/src/locales/ro.json b/vue3/src/locales/ro.json index c8eb2358a..5dc00e700 100644 --- a/vue3/src/locales/ro.json +++ b/vue3/src/locales/ro.json @@ -1,4 +1,5 @@ { + "AISettingsHostedHelp": "", "API": "API", "API_Browser": "", "API_Documentation": "", @@ -17,6 +18,7 @@ "Added_on": "Adăugat la", "Advanced": "Avansat", "Advanced Search Settings": "", + "AiCreditsBalance": "", "AiLog": "", "AiLogHelp": "", "AiModelHelp": "", @@ -128,6 +130,7 @@ "FoodOnHand": "Aveți {food} la îndemână.", "Food_Alias": "Pseudonim mâncare", "Foods": "Alimente", + "FromBalance": "", "Fulltext": "", "FulltextHelp": "", "Fuzzy": "", @@ -202,6 +205,7 @@ "Message": "Mesaj", "MissingProperties": "", "Month": "Lună", + "MonthlyCredits": "", "MonthlyCreditsUsed": "", "Move": "Mută", "MoveCategory": "Mută la: ", @@ -301,6 +305,7 @@ "Selected": "Selectat", "Servings": "Porții", "Settings": "Setări", + "SettingsOnlySuperuser": "", "Share": "Împărtășire", "Shopping_Categories": "Categorii de cumpărături", "Shopping_Category": "Categorie de cumpărături", diff --git a/vue3/src/locales/ru.json b/vue3/src/locales/ru.json index 77ce89eba..f83b5ffd9 100644 --- a/vue3/src/locales/ru.json +++ b/vue3/src/locales/ru.json @@ -1,6 +1,7 @@ { "AI": "AI", "AIImportSubtitle": "Используй AI для импорта изображений рецептов.", + "AISettingsHostedHelp": "", "API": "API", "API_Browser": "", "API_Documentation": "", @@ -27,6 +28,7 @@ "Admin": "Админ", "Advanced": "Расширенный", "Advanced Search Settings": "", + "AiCreditsBalance": "", "AiLog": "", "AiLogHelp": "", "AiModelHelp": "", @@ -210,6 +212,7 @@ "Food_Replace": "Замена продукта", "Foods": "Продукты", "Friday": "Пятница", + "FromBalance": "", "Fulltext": "Полнотекстовый", "FulltextHelp": "Поля, используемые в полнотекстовом поиске. Важно: методы поиска web, phrase и raw применимы только к полнотекстовым полям.", "Fuzzy": "Нечёткий", @@ -313,6 +316,7 @@ "ModelSelectResultsHelp": "Показать больше результатов", "Monday": "Понедельник", "Month": "Месяц", + "MonthlyCredits": "", "MonthlyCreditsUsed": "", "More": "Ещё", "Move": "Переместить", @@ -466,6 +470,7 @@ "Servings": "Порции", "ServingsText": "Описание порций", "Settings": "Настройки", + "SettingsOnlySuperuser": "", "Share": "Поделиться", "ShopLater": "Купить позже", "ShopNow": "Купить сейчас", diff --git a/vue3/src/locales/sl.json b/vue3/src/locales/sl.json index 8effaf7a2..91e841edd 100644 --- a/vue3/src/locales/sl.json +++ b/vue3/src/locales/sl.json @@ -1,6 +1,7 @@ { "AI": "Umetna inteligenca", "AIImportSubtitle": "Uporabite umetno inteligenco za uvoz slik receptov.", + "AISettingsHostedHelp": "", "API": "API", "API_Browser": "", "API_Documentation": "", @@ -27,6 +28,7 @@ "Admin": "Skrbnik", "Advanced": "Napredno", "Advanced Search Settings": "", + "AiCreditsBalance": "", "AiLog": "", "AiLogHelp": "", "AiModelHelp": "", @@ -210,6 +212,7 @@ "Food_Replace": "Zamenjava živila", "Foods": "Živila", "Friday": "Petek", + "FromBalance": "", "Fulltext": "Celotno besedilo", "FulltextHelp": "Polja za iskanje po celotnem besedilu. Opomba: metode iskanja »splet«, »fraza« in »surovo« delujejo samo s polji po celotnem besedilu.", "Fuzzy": "Nejasno", @@ -314,6 +317,7 @@ "ModelSelectResultsHelp": "Išči več rezultatov", "Monday": "Ponedeljek", "Month": "Mesec", + "MonthlyCredits": "", "MonthlyCreditsUsed": "", "More": "Več", "Move": "Premakni", @@ -468,6 +472,7 @@ "Servings": "Porcije", "ServingsText": "Besedilo o porcijah", "Settings": "Nastavitve", + "SettingsOnlySuperuser": "", "Share": "Deli", "ShopLater": "Nakupujte pozneje", "ShopNow": "Nakupujte zdaj", diff --git a/vue3/src/locales/sv.json b/vue3/src/locales/sv.json index 1de5b68d0..4ef087992 100644 --- a/vue3/src/locales/sv.json +++ b/vue3/src/locales/sv.json @@ -1,5 +1,6 @@ { "AIImportSubtitle": "Använd AI för att importera bilder av recept.", + "AISettingsHostedHelp": "", "API": "API", "API_Browser": "", "API_Documentation": "", @@ -26,6 +27,7 @@ "Added_on": "Tillagd på", "Admin": "Administratör", "Advanced": "Avancerat", + "AiCreditsBalance": "", "AiLog": "", "AiLogHelp": "", "AiModelHelp": "", @@ -187,6 +189,7 @@ "Food_Alias": "Alias för livsmedel", "Food_Replace": "Ersätt ingrediens", "Foods": "Livsmedel", + "FromBalance": "", "Fulltext": "", "FulltextHelp": "", "Fuzzy": "", @@ -264,6 +267,7 @@ "Message": "Meddelande", "MissingProperties": "", "Month": "Månad", + "MonthlyCredits": "", "MonthlyCreditsUsed": "", "Move": "Flytta", "MoveCategory": "Flytta till: ", @@ -377,6 +381,7 @@ "Selected": "Vald", "Servings": "Portioner", "Settings": "Inställningar", + "SettingsOnlySuperuser": "", "Share": "Dela", "ShoppingBackgroundSyncWarning": "Dålig uppkoppling, inväntar synkronisering...", "Shopping_Categories": "Shopping kategorier", diff --git a/vue3/src/locales/tr.json b/vue3/src/locales/tr.json index a7d0c5154..21b70179f 100644 --- a/vue3/src/locales/tr.json +++ b/vue3/src/locales/tr.json @@ -1,4 +1,5 @@ { + "AISettingsHostedHelp": "", "API": "API", "API_Browser": "", "API_Documentation": "", @@ -16,6 +17,7 @@ "Added_by": "Ekleyen", "Added_on": "Eklenme Zamanı", "Advanced": "Gelişmiş", + "AiCreditsBalance": "", "AiLog": "", "AiLogHelp": "", "AiModelHelp": "", @@ -150,6 +152,7 @@ "Food_Alias": "Yiyecek Takma Adı", "Food_Replace": "Yiyecek Değiştir", "Foods": "Yiyecekler", + "FromBalance": "", "Fulltext": "", "FulltextHelp": "", "Fuzzy": "", @@ -227,6 +230,7 @@ "Message": "Mesaj", "MissingProperties": "", "Month": "Ay", + "MonthlyCredits": "", "MonthlyCreditsUsed": "", "Move": "Taşı", "MoveCategory": "Taşı: ", @@ -340,6 +344,7 @@ "Selected": "Seçilen", "Servings": "Servis Sayısı", "Settings": "Ayarlar", + "SettingsOnlySuperuser": "", "Share": "Paylaş", "ShoppingBackgroundSyncWarning": "Kötü bağlantı, senkronizasyon bekleniyor...", "Shopping_Categories": "Alışveriş Kategorileri", diff --git a/vue3/src/locales/uk.json b/vue3/src/locales/uk.json index c4355b18b..c5bbd47ca 100644 --- a/vue3/src/locales/uk.json +++ b/vue3/src/locales/uk.json @@ -1,4 +1,5 @@ { + "AISettingsHostedHelp": "", "API_Browser": "", "API_Documentation": "", "Add": "Додати", @@ -14,6 +15,7 @@ "Added_by": "Додано", "Added_on": "Додано На", "Advanced": "", + "AiCreditsBalance": "", "AiLog": "", "AiLogHelp": "", "AiModelHelp": "", @@ -131,6 +133,7 @@ "FoodOnHand": "Ви маєте {food} на руках.", "Food_Alias": "Найменування Їжі", "Foods": "", + "FromBalance": "", "Fulltext": "", "FulltextHelp": "", "Fuzzy": "", @@ -199,6 +202,7 @@ "Merge_Keyword": "Об'єднати Ключове слово", "MissingProperties": "", "Month": "Місяць", + "MonthlyCredits": "", "MonthlyCreditsUsed": "", "Move": "Перемістити", "MoveCategory": "Перемістити До: ", @@ -302,6 +306,7 @@ "Selected": "Вибрано", "Servings": "Порції", "Settings": "Налаштування", + "SettingsOnlySuperuser": "", "Share": "Поділитися", "Shopping_Categories": "Категорії Покупок", "Shopping_Category": "Категорія Покупок", diff --git a/vue3/src/locales/zh_Hans.json b/vue3/src/locales/zh_Hans.json index bee29c59e..bf0a515fb 100644 --- a/vue3/src/locales/zh_Hans.json +++ b/vue3/src/locales/zh_Hans.json @@ -1,4 +1,5 @@ { + "AISettingsHostedHelp": "", "API": "API", "API_Browser": "", "API_Documentation": "", @@ -16,6 +17,7 @@ "Added_by": "添加者", "Added_on": "添加到", "Advanced": "高级", + "AiCreditsBalance": "", "AiLog": "", "AiLogHelp": "", "AiModelHelp": "", @@ -150,6 +152,7 @@ "Food_Alias": "食物别名", "Food_Replace": "食物替换", "Foods": "食物", + "FromBalance": "", "Fulltext": "", "FulltextHelp": "", "Fuzzy": "", @@ -227,6 +230,7 @@ "Message": "信息", "MissingProperties": "", "Month": "月份", + "MonthlyCredits": "", "MonthlyCreditsUsed": "", "Move": "移动", "MoveCategory": "移动到: ", @@ -340,6 +344,7 @@ "Selected": "选定", "Servings": "份量", "Settings": "设置", + "SettingsOnlySuperuser": "", "Share": "分享", "ShoppingBackgroundSyncWarning": "网络状况不佳,正在等待进行同步……", "Shopping_Categories": "购物类别", diff --git a/vue3/src/locales/zh_Hant.json b/vue3/src/locales/zh_Hant.json index 75d11accb..ade07771a 100644 --- a/vue3/src/locales/zh_Hant.json +++ b/vue3/src/locales/zh_Hant.json @@ -1,6 +1,7 @@ { "AI": "人工智慧", "AIImportSubtitle": "以人工智慧匯入食譜圖片。", + "AISettingsHostedHelp": "", "API": "API", "API_Browser": "", "API_Documentation": "", @@ -26,6 +27,7 @@ "Added_on": "添加於", "Admin": "管理者", "Advanced": "高級", + "AiCreditsBalance": "", "AiLog": "", "AiLogHelp": "", "AiModelHelp": "", @@ -209,6 +211,7 @@ "Food_Replace": "食物替換", "Foods": "食物", "Friday": "星期五", + "FromBalance": "", "Fulltext": "全文", "FulltextHelp": "全文搜索的字段。注意:'web'、'phrase' 和 'raw' 搜索方法僅對全文欄位有效。", "Fuzzy": "模糊", @@ -313,6 +316,7 @@ "ModelSelectResultsHelp": "搜尋更多結果", "Monday": "星期一", "Month": "月", + "MonthlyCredits": "", "MonthlyCreditsUsed": "", "More": "更多", "Move": "移動", @@ -467,6 +471,7 @@ "Servings": "份量", "ServingsText": "份量文字", "Settings": "設定", + "SettingsOnlySuperuser": "", "Share": "分享", "ShopLater": "稍後購物", "ShopNow": "立即購物", diff --git a/vue3/src/openapi/apis/ApiApi.ts b/vue3/src/openapi/apis/ApiApi.ts index 328adbb98..7d74fb32a 100644 --- a/vue3/src/openapi/apis/ApiApi.ts +++ b/vue3/src/openapi/apis/ApiApi.ts @@ -1785,7 +1785,7 @@ export interface ApiSpaceListRequest { export interface ApiSpacePartialUpdateRequest { id: number; - patchedSpace?: Omit; + patchedSpace?: Omit; } export interface ApiSpaceRetrieveRequest { diff --git a/vue3/src/openapi/models/PatchedSpace.ts b/vue3/src/openapi/models/PatchedSpace.ts index 1d8ed9700..8d9f773da 100644 --- a/vue3/src/openapi/models/PatchedSpace.ts +++ b/vue3/src/openapi/models/PatchedSpace.ts @@ -31,6 +31,12 @@ import { SpaceNavTextColorEnumFromJSONTyped, SpaceNavTextColorEnumToJSON, } from './SpaceNavTextColorEnum'; +import type { AiProvider } from './AiProvider'; +import { + AiProviderFromJSON, + AiProviderFromJSONTyped, + AiProviderToJSON, +} from './AiProvider'; import type { FoodInheritField } from './FoodInheritField'; import { FoodInheritFieldFromJSON, @@ -217,13 +223,13 @@ export interface PatchedSpace { * @type {number} * @memberof PatchedSpace */ - readonly aiCreditsMonthly?: number; + aiCreditsMonthly?: number; /** * * @type {number} * @memberof PatchedSpace */ - readonly aiCreditsBalance?: number; + aiCreditsBalance?: number; /** * * @type {number} @@ -236,6 +242,12 @@ export interface PatchedSpace { * @memberof PatchedSpace */ aiEnabled?: boolean; + /** + * + * @type {AiProvider} + * @memberof PatchedSpace + */ + aiDefaultProvider?: AiProvider; } /** @@ -286,10 +298,11 @@ export function PatchedSpaceFromJSONTyped(json: any, ignoreDiscriminator: boolea 'aiCreditsBalance': json['ai_credits_balance'] == null ? undefined : json['ai_credits_balance'], 'aiMonthlyCreditsUsed': json['ai_monthly_credits_used'] == null ? undefined : json['ai_monthly_credits_used'], 'aiEnabled': json['ai_enabled'] == null ? undefined : json['ai_enabled'], + 'aiDefaultProvider': json['ai_default_provider'] == null ? undefined : AiProviderFromJSON(json['ai_default_provider']), }; } -export function PatchedSpaceToJSON(value?: Omit | null): any { +export function PatchedSpaceToJSON(value?: Omit | null): any { if (value == null) { return value; } @@ -312,7 +325,10 @@ export function PatchedSpaceToJSON(value?: Omit | null): any { +export function SpaceToJSON(value?: Omit | null): any { if (value == null) { return value; } @@ -326,7 +337,10 @@ export function SpaceToJSON(value?: Omit(null) const image = ref(null) const aiMode = ref<'file' | 'text'>('file') -const selectedAiProvider = ref(undefined) +const selectedAiProvider = ref(useUserPreferenceStore().activeSpace.aiDefaultProvider) const editAfterImport = ref(false) const bookmarkletToken = ref("") diff --git a/vue3/src/types/Models.ts b/vue3/src/types/Models.ts index ecb24e05a..5061e9fa1 100644 --- a/vue3/src/types/Models.ts +++ b/vue3/src/types/Models.ts @@ -834,6 +834,7 @@ export const TAiLog = { {title: 'Type', key: '_function'}, {title: 'AiProvider', key: 'aiProvider.name',}, {title: 'Credits', key: 'creditCost',}, + {title: 'FromBalance', key: 'creditsFromBalance',}, {title: 'CreatedAt', key: 'createdAt'}, {title: 'Actions', key: 'action', align: 'end'}, ]