Skip to content

Commit d710ddc

Browse files
committed
WIP
1 parent 03afb4a commit d710ddc

33 files changed

Lines changed: 855 additions & 34 deletions
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
class Localization::GlossaryEntry::Create
2+
include Mandate
3+
4+
initialize_with :locale, :term, :translation, :llm_instructions
5+
6+
def call
7+
Localization::GlossaryEntry.create!(
8+
locale:,
9+
term:,
10+
translation:,
11+
llm_instructions:
12+
)
13+
end
14+
end
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
class Localization::GlossaryEntryProposal::Approve
2+
include Mandate
3+
4+
initialize_with :proposal, :user
5+
6+
def call
7+
ActiveRecord::Base.transaction do
8+
proposal.update!(
9+
status: :approved,
10+
reviewer: user
11+
)
12+
handle_glossary_update!
13+
end
14+
end
15+
16+
private
17+
def handle_glossary_update!
18+
send("handle_#{proposal.type.to_s.camelize(:lower)}!")
19+
end
20+
21+
def handle_addition!
22+
raise "Glossary entry already exists" if Localization::GlossaryEntry.exists?(locale: proposal.locale, term: proposal.term)
23+
24+
Localization::GlossaryEntry.create!(
25+
locale: proposal.locale,
26+
term: proposal.term,
27+
translation: proposal.translation,
28+
llm_instructions: proposal.llm_instructions
29+
)
30+
end
31+
32+
def handle_modification!
33+
raise "No glossary entry to modify" if glossary_entry.nil?
34+
35+
glossary_entry.update!(
36+
translation: proposal.translation,
37+
llm_instructions: proposal.llm_instructions
38+
)
39+
end
40+
41+
def handle_deletion!
42+
raise "No glossary entry to delete" if glossary_entry.nil?
43+
44+
# Retrieve this before destroying else we have a race
45+
glossary_entry = proposal.glossary_entry
46+
47+
# Disassociate before deletion to avoid FK issues
48+
proposal.update!(glossary_entry: nil)
49+
glossary_entry.destroy!
50+
end
51+
52+
delegate :glossary_entry, to: :proposal
53+
end
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
class Localization::GlossaryEntryProposal
2+
class CreateAddition
3+
include Mandate
4+
5+
initialize_with :term, :locale, :user, :translation, :llm_instructions
6+
7+
def call
8+
ActiveRecord::Base.transaction do
9+
Localization::GlossaryEntryProposal.create!(
10+
type: :addition,
11+
proposer: user,
12+
term: term,
13+
locale: locale,
14+
translation: translation,
15+
llm_instructions: llm_instructions
16+
)
17+
end.tap do |proposal| # rubocop:disable Style/MultilineBlockChain
18+
VerifyWithLLM.defer(proposal)
19+
end
20+
end
21+
end
22+
end
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
class Localization::GlossaryEntryProposal
2+
class CreateDeletion
3+
include Mandate
4+
5+
initialize_with :glossary_entry, :user
6+
7+
def call
8+
ActiveRecord::Base.transaction do
9+
Localization::GlossaryEntryProposal.create!(
10+
type: :deletion,
11+
glossary_entry: glossary_entry,
12+
proposer: user
13+
)
14+
end
15+
end
16+
end
17+
end
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
class Localization::GlossaryEntryProposal
2+
class CreateModification
3+
include Mandate
4+
5+
initialize_with :glossary_entry, :user, :translation, :llm_instructions
6+
7+
def call
8+
ActiveRecord::Base.transaction do
9+
Localization::GlossaryEntryProposal.create!(
10+
type: :modification,
11+
glossary_entry: glossary_entry,
12+
proposer: user,
13+
translation: translation,
14+
llm_instructions: llm_instructions
15+
)
16+
end.tap do |proposal| # rubocop:disable Style/MultilineBlockChain
17+
VerifyWithLLM.defer(proposal)
18+
end
19+
end
20+
end
21+
end
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
class Localization::GlossaryEntryProposal::Reject
2+
include Mandate
3+
4+
initialize_with :proposal, :user
5+
6+
def call
7+
ActiveRecord::Base.transaction do
8+
proposal.update!(
9+
status: :rejected,
10+
reviewer: user
11+
)
12+
end
13+
end
14+
15+
delegate :glossary_entry, to: :proposal
16+
end
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
class Localization::GlossaryEntryProposal
2+
class UpdateValue
3+
include Mandate
4+
5+
initialize_with :proposal, :user, :term, :translation, :llm_instructions
6+
7+
def call
8+
proposal.update!(term:, translation:, llm_instructions:)
9+
10+
VerifyWithLLM.defer(proposal)
11+
end
12+
end
13+
end
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
class Localization::GlossaryEntryProposal::VerifyWithLLM
2+
include Mandate
3+
4+
initialize_with :proposal
5+
6+
def call
7+
LLM::ExecGeminiFlash.(prompt, endpoint)
8+
end
9+
10+
def endpoint
11+
"localization_verify_glossary_entry_proposal?proposal_uuid=#{proposal.uuid}"
12+
end
13+
14+
def prompt
15+
<<~PROMPT
16+
You are a localization expert.#{' '}
17+
Your task is to verify the quality of a glossary entry proposal.
18+
This glossary entry will be sent to future LLM translators when doing translation work.
19+
20+
The proposal has three parts:
21+
- A term in English
22+
- A proposed translation of that term into the target language
23+
- Proposed instructions on how the term should be used by the LLM translator in the future
24+
25+
Respond with JSON containing two fields:
26+
- `result`: "approved", "rejected" or "spam" based on the quality of the glossary entry.
27+
- `reason`: A brief explanation of your decision.
28+
29+
You should use "approved" if the glossary entry is generally accurate and appropriate for the given context.
30+
You should use "rejected" if the glossary entry has issues such as inaccuracies, poor phrasing, or is not suitable for the context.
31+
You should use "spam" if the glossary entry is a serious step away from the intended purpose. For example, if it is trying to inject spam onto the website, or contains profanity etc. Use this with care as it will automatically block the user that proposed the change.
32+
33+
The target locale is `#{proposal.locale}`
34+
35+
The original glossary term was:
36+
~~~~~~
37+
#{proposal.term}
38+
~~~~~~
39+
40+
This is information about how it's used:
41+
~~~~~~
42+
#{proposal.llm_instructions}
43+
~~~~~~
44+
45+
The proposed glossary entry is:
46+
~~~~~~
47+
#{proposal.translation}
48+
~~~~~~
49+
50+
Respond with JSON.
51+
PROMPT
52+
end
53+
end

app/commands/localization/translation_proposal/create.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ def call
1313
modified_from_llm: true
1414
)
1515
end.tap do |proposal| # rubocop:disable Style/MultilineBlockChain
16-
VerifyWithLLM.(proposal)
16+
VerifyWithLLM.defer(proposal)
1717
end
1818
end
1919
end

app/commands/localization/translation_proposal/verify_with_llm.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ def call
88
end
99

1010
def endpoint
11-
"localization_verify_llm_proposal?proposal_uuid=#{proposal.uuid}"
11+
"localization_verify_translation_proposal?proposal_uuid=#{proposal.uuid}"
1212
end
1313

1414
def prompt

0 commit comments

Comments
 (0)