mirror of
https://github.com/HeyPuter/puter.git
synced 2026-05-29 12:50:59 +00:00
Merge branch 'main' into feature/issue-#975/finish-French-translations
This commit is contained in:
@@ -6,6 +6,7 @@ const { DB_WRITE } = require("../../services/database/consts");
|
||||
const { TypeSpec } = require("../../services/drivers/meta/Construct");
|
||||
const { TypedValue } = require("../../services/drivers/meta/Runtime");
|
||||
const { Context } = require("../../util/context");
|
||||
const { AsModeration } = require("./lib/AsModeration");
|
||||
|
||||
// Maximum number of fallback attempts when a model fails, including the first attempt
|
||||
const MAX_FALLBACKS = 3 + 1; // includes first attempt
|
||||
@@ -278,6 +279,7 @@ class AIChatService extends BaseService {
|
||||
intended_service,
|
||||
parameters
|
||||
};
|
||||
await svc_event.emit('ai.prompt.validate', event);
|
||||
if ( ! event.allow ) {
|
||||
test_mode = true;
|
||||
}
|
||||
@@ -489,11 +491,6 @@ class AIChatService extends BaseService {
|
||||
* Returns true if OpenAI service is unavailable or all messages pass moderation.
|
||||
*/
|
||||
async moderate ({ messages }) {
|
||||
const svc_openai = this.services.get('openai-completion');
|
||||
|
||||
// We can't use moderation of openai service isn't available
|
||||
if ( ! svc_openai ) return true;
|
||||
|
||||
for ( const msg of messages ) {
|
||||
const texts = [];
|
||||
if ( typeof msg.content === 'string' ) texts.push(msg.content);
|
||||
@@ -508,8 +505,41 @@ class AIChatService extends BaseService {
|
||||
|
||||
const fulltext = texts.join('\n');
|
||||
|
||||
const mod_result = await svc_openai.check_moderation(fulltext);
|
||||
if ( mod_result.flagged ) return false;
|
||||
let mod_last_error = null;
|
||||
let mod_result = null;
|
||||
try {
|
||||
const svc_openai = this.services.get('openai-completion');
|
||||
mod_result = await svc_openai.check_moderation(fulltext);
|
||||
if ( mod_result.flagged ) return false;
|
||||
continue;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
mod_last_error = e;
|
||||
}
|
||||
try {
|
||||
const svc_claude = this.services.get('claude');
|
||||
const chat = svc_claude.as('puter-chat-completion');
|
||||
const mod = new AsModeration({
|
||||
chat,
|
||||
model: 'claude-3-haiku-20240307',
|
||||
})
|
||||
if ( ! await mod.moderate(fulltext) ) {
|
||||
return false;
|
||||
}
|
||||
mod_last_error = null;
|
||||
continue;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
mod_last_error = e;
|
||||
}
|
||||
|
||||
if ( mod_last_error ) {
|
||||
this.log.error('moderation error', {
|
||||
fulltext,
|
||||
mod_last_error,
|
||||
});
|
||||
throw new Error('no working moderation service');
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -65,6 +65,10 @@ class FakeChatService extends BaseService {
|
||||
"output_tokens": 1
|
||||
}
|
||||
},
|
||||
"usage": {
|
||||
"input_tokens": 0,
|
||||
"output_tokens": 1
|
||||
},
|
||||
"logprobs": null,
|
||||
"finish_reason": "stop"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
const { default: dedent } = require("dedent");
|
||||
|
||||
class AsModeration {
|
||||
constructor ({ chat, model }) {
|
||||
this.chat = chat;
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
async moderate (text) {
|
||||
// copied from Anthropic documentation
|
||||
// https://docs.anthropic.com/en/docs/about-claude/use-case-guides/content-moderation
|
||||
const unsafe_categories = [
|
||||
'Child Exploitation',
|
||||
'Conspiracy Theories',
|
||||
'Hate',
|
||||
'Indiscriminate Weapons',
|
||||
'Intellectual Property',
|
||||
'Non-Violent Crimes',
|
||||
'Privacy',
|
||||
'Self-Harm',
|
||||
'Sex Crimes',
|
||||
'Sexual Content',
|
||||
'Specialized Advice',
|
||||
'Violent Crimes'
|
||||
];
|
||||
|
||||
const unsafe_category_str = unsafe_categories.join('\n');
|
||||
|
||||
const assessment_prompt = dedent(`
|
||||
Determine whether the following message warrants moderation,
|
||||
based on the unsafe categories outlined below.
|
||||
|
||||
Message:
|
||||
<message>
|
||||
${text
|
||||
.replace('<', '<')
|
||||
.replace('>', '>')
|
||||
}
|
||||
</message>
|
||||
|
||||
Unsafe Categories:
|
||||
<categories>
|
||||
${unsafe_category_str}
|
||||
</categories>
|
||||
|
||||
Respond with ONLY a JSON object, using the format below:
|
||||
{{
|
||||
"violation": <Boolean field denoting whether the message should be moderated>,
|
||||
"categories": [Comma-separated list of violated categories],
|
||||
"explanation": [Optional. Only include if there is a violation.]
|
||||
}}
|
||||
`);
|
||||
|
||||
const result = await this.chat.complete({
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: assessment_prompt,
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
console.log('result???', require('util').inspect(result, { depth: null }));
|
||||
|
||||
const str = result.message?.content?.[0]?.text ??
|
||||
result.messages?.[0]?.content?.[0]?.text ??
|
||||
'{ "violation": true }';
|
||||
|
||||
const parsed = JSON.parse(str);
|
||||
console.log('parsed?', parsed);
|
||||
return ! parsed.violation;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
AsModeration,
|
||||
};
|
||||
@@ -169,7 +169,7 @@
|
||||
<!---------------------------------------->
|
||||
<!-- Edit App -->
|
||||
<!---------------------------------------->
|
||||
<section id="edit-app">
|
||||
<section id="edit-app" style="margin-bottom: 100px;">
|
||||
</section>
|
||||
|
||||
<!---------------------------------------->
|
||||
|
||||
@@ -518,9 +518,11 @@ function generate_edit_app_section(app) {
|
||||
</div>
|
||||
|
||||
<div class="section-tab" data-tab="info">
|
||||
<form style="clear:both;">
|
||||
<form style="clear:both; padding-bottom: 50px;">
|
||||
<div class="error" id="edit-app-error"></div>
|
||||
<div class="success" id="edit-app-success">App has been successfully updated.<span class="close-success-msg">×</span></div>
|
||||
<div class="success" id="edit-app-success">App has been successfully updated.<span class="close-success-msg">×</span>
|
||||
<p style="margin-bottom:0;"><span class="open-app button button-action" data-uid="${html_encode(app.uid)}" data-app-name="${html_encode(app.name)}">Give it a try!</span></p>
|
||||
</div>
|
||||
<input type="hidden" id="edit-app-uid" value="${html_encode(app.uid)}">
|
||||
|
||||
<h3 style="font-size: 23px; border-bottom: 1px solid #EEE; margin-top: 40px;">Basic</h3>
|
||||
@@ -612,9 +614,10 @@ function generate_edit_app_section(app) {
|
||||
<p><code>credentialless</code> attribute for the <code>iframe</code> tag.</p>
|
||||
</div>
|
||||
|
||||
<hr style="margin-top: 40px;">
|
||||
<button type="button" class="edit-app-save-btn button button-primary">Save</button>
|
||||
<button type="button" class="edit-app-reset-btn button button-secondary">Reset</button>
|
||||
<div style="box-shadow: 10px 10px 15px #8c8c8c; overflow: hidden; position: fixed; bottom: 0; background: white; padding: 10px; width: 100%; left: 0;">
|
||||
<button type="button" class="edit-app-save-btn button button-primary" style="margin-right: 40px;">Save</button>
|
||||
<button type="button" class="edit-app-reset-btn button button-secondary">Reset</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
`
|
||||
|
||||
@@ -364,21 +364,21 @@ const fr = {
|
||||
"This user already has access to this item": 'Cet utilisateur à déja accès à cet élément',
|
||||
|
||||
// ----------------------------------------
|
||||
// Missing translations:
|
||||
// translations:
|
||||
// ----------------------------------------
|
||||
"billing.change_payment_method": "Modifier", // In English: "Change"
|
||||
"billing.change_payment_method": "Modifier le mode de paiement", // In English: "Change"
|
||||
"billing.cancel": "Annuler", // In English: "Cancel"
|
||||
"billing.download_invoice": "Télécharger la facture", // In English: "Download"
|
||||
"billing.payment_method": "Méthode de paiement", // In English: "Payment Method"
|
||||
"billing.payment_method_updated": "Méthode de paiement mise à jour !", // In English: "Payment method updated!"
|
||||
"billing.confirm_payment_method": "Confirmer la méthode de paiement", // In English: "Confirm Payment Method"
|
||||
"billing.payment_method": "Mode de paiement", // In English: "Payment Method"
|
||||
"billing.payment_method_updated": "Mode de paiement mis à jour !", // In English: "Payment method updated!"
|
||||
"billing.confirm_payment_method": "Confirmer le mode de paiement", // In English: "Confirm Payment Method"
|
||||
"billing.payment_history": "Historique des paiements", // In English: "Payment History"
|
||||
"billing.refunded": "Remboursé", // In English: "Refunded"
|
||||
"billing.paid": "Payé", // In English: "Paid"
|
||||
"billing.ok": "OK", // In English: "OK"
|
||||
"billing.resume_subscription": "Reprendre l'abonnement", // In English: "Resume Subscription"
|
||||
"billing.subscription_cancelled": "Votre abonnement a été annulé.", // In English: "Your subscription has been canceled."
|
||||
"billing.subscription_cancelled_description": "Vous conserverez l'accès à votre abonnement jusqu'à la fin de cette période de facturation.", // In English: "You will still have access to your subscription until the end of this billing period."
|
||||
"billing.subscription_cancelled_description": "Vous aurez toujours accès à votre abonnement jusqu'à la fin de cette période de facturation.", // In English: "You will still have access to your subscription until the end of this billing period."
|
||||
"billing.offering.free": "Gratuit", // In English: "Free"
|
||||
"billing.offering.pro": "Professionnel", // In English: "Professional"
|
||||
"billing.offering.business": "Entreprise", // In English: "Business"
|
||||
@@ -386,24 +386,24 @@ const fr = {
|
||||
"billing.ai_access": "Accès à l'IA", // In English: "AI Access"
|
||||
"billing.bandwidth": "Bande passante", // In English: "Bandwidth"
|
||||
"billing.apps_and_games": "Applications et jeux", // In English: "Apps & Games"
|
||||
"billing.upgrade_to_pro": "Passer à la version %strong%", // In English: "Upgrade to %strong%"
|
||||
"billing.switch_to": "Optez pour %strong%", // In English: "Switch to %strong%"
|
||||
"billing.payment_setup": "Configuration des paiements", // In English: "Payment Setup"
|
||||
"billing.upgrade_to_pro": "Passer à %strong%", // In English: "Upgrade to %strong%"
|
||||
"billing.switch_to": "Passer à %strong%", // In English: "Switch to %strong%"
|
||||
"billing.payment_setup": "Configuration du paiement", // In English: "Payment Setup"
|
||||
"billing.back": "Retour", // In English: "Back"
|
||||
"billing.you_are_now_subscribed_to": "Vous êtes désormais abonné au niveau %strong%.", // In English: "You are now subscribed to %strong% tier."
|
||||
"billing.you_are_now_subscribed_to_without_tier": "Vous êtes désormais abonné", // In English: "You are now subscribed"
|
||||
"billing.subscription_cancellation_confirmation": "Êtes-vous certain de vouloir annuler votre abonnement ?", // In English: "Are you sure you want to cancel your subscription?"
|
||||
"billing.you_are_now_subscribed_to": "Vous êtes maintenant abonné au niveau %strong%.", // In English: "You are now subscribed to %strong% tier."
|
||||
"billing.you_are_now_subscribed_to_without_tier": "Vous êtes maintenant abonné", // In English: "You are now subscribed"
|
||||
"billing.subscription_cancellation_confirmation": "Êtes-vous sûr de vouloir annuler votre abonnement ?", // In English: "Are you sure you want to cancel your subscription?"
|
||||
"billing.subscription_setup": "Configuration de l'abonnement", // In English: "Subscription Setup"
|
||||
"billing.cancel_it": "Annuler", // In English: "Cancel It"
|
||||
"billing.keep_it": "Conserver", // In English: "Keep It"
|
||||
"billing.subscription_resumed": "Votre abonnement %strong% a été réactivé !", // In English: "Your %strong% subscription has been resumed!"
|
||||
"billing.cancel_it": "L'annuler", // In English: "Cancel It"
|
||||
"billing.keep_it": "Le conserver", // In English: "Keep It"
|
||||
"billing.subscription_resumed": "Votre abonnement %strong% a été repris !", // In English: "Your %strong% subscription has been resumed!"
|
||||
"billing.upgrade_now": "Mettre à niveau maintenant", // In English: "Upgrade Now"
|
||||
"billing.upgrade": "Mettre à niveau", // In English: "Upgrade"
|
||||
"billing.currently_on_free_plan": "Vous êtes actuellement sur le plan gratuit.", // In English: "You are currently on the free plan."
|
||||
"billing.download_receipt": "Télécharger le reçu", // In English: "Download Receipt"
|
||||
"billing.subscription_check_error": "Un problème est survenu lors de la vérification de votre abonnement.", // In English: "A problem occurred while checking your subscription status."
|
||||
"billing.email_confirmation_needed": "Votre e-mail n'a pas été confirmé. Nous vous enverrons un code pour le confirmer maintenant.", // In English: "Your email has not been confirmed. We'll send you a code to confirm it now."
|
||||
"billing.sub_cancelled_but_valid_until": "Vous avez annulé votre abonnement, mais il restera actif jusqu'à la fin de la période de facturation. Vous ne serez pas facturé à nouveau, sauf si vous vous réabonnez.", // In English: "You have cancelled your subscription and it will automatically switch to the free tier at the end of the billing period. You will not be charged again unless you re-subscribe."
|
||||
"billing.subscription_check_error": "Un problème est survenu lors de la vérification de votre statut d'abonnement.", // In English: "A problem occurred while checking your subscription status."
|
||||
"billing.email_confirmation_needed": "Votre e-mail n'a pas été confirmé. Nous allons vous envoyer un code pour le confirmer maintenant.", // In English: "Your email has not been confirmed. We'll send you a code to confirm it now."
|
||||
"billing.sub_cancelled_but_valid_until": "Vous avez annulé votre abonnement et il passera automatiquement au niveau gratuit à la fin de la période de facturation. Vous ne serez pas facturé à nouveau sauf si vous vous réabonnez.", // In English: "You have cancelled your subscription and it will automatically switch to the free tier at the end of the billing period. You will not be charged again unless you re-subscribe."
|
||||
"billing.current_plan_until_end_of_period": "Votre plan actuel jusqu'à la fin de cette période de facturation.", // In English: "Your current plan until the end of this billing period."
|
||||
"billing.current_plan": "Plan actuel", // In English: "Current plan"
|
||||
"billing.cancelled_subscription_tier": "Abonnement annulé (%%)", // In English: "Cancelled Subscription (%%)"
|
||||
@@ -411,8 +411,7 @@ const fr = {
|
||||
"billing.limited": "Limité", // In English: "Limited"
|
||||
"billing.expanded": "Étendu", // In English: "Expanded"
|
||||
"billing.accelerated": "Accéléré", // In English: "Accelerated"
|
||||
"billing.enjoy_msg": "Profitez de %% de stockage cloud, ainsi que d'autres avantages." // In English: "Enjoy %% of Cloud Storage plus other benefits."
|
||||
|
||||
"billing.enjoy_msg": "Profitez de %% de stockage cloud et d'autres avantages.", // In English: "Enjoy %% of Cloud Storage plus other benefits."
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -435,6 +435,7 @@ const ko = {
|
||||
"billing.current_plan_until_end_of_period":
|
||||
"청구 기간이 끝날 때까지 유지되는 현재 플랜입니다.", // In English: "Your current plan until the end of this billing period."
|
||||
"billing.current_plan": "현재 플랜", // In English: "Current plan" ; depending on the context you could use: "구독 중인 플랜" (plan you are subscribed to)
|
||||
|
||||
"billing.cancelled_subscription_tier": "취소된 구독 (%%)", // In English: "Cancelled Subscription (%%)"
|
||||
"billing.manage": "관리", // In English: "Manage"
|
||||
"billing.limited": "제한됨", // In English: "Limited"
|
||||
|
||||
Reference in New Issue
Block a user