GitLab

accessible_worlds.aw_build_prompt

  1from spacy.util import raise_error
  2from .aw_vocabulary import AwVocabulary
  3from .aw_utils import (
  4    split_sentences
  5)
  6
  7from typing import Literal
  8
  9import numpy as np
 10
 11def assemble_list( x : list[str], last : str ='and' ) :
 12    
 13    res = ""
 14    n = len(x)
 15
 16    if n == 0 :
 17        return res
 18    elif n == 1 :
 19        return f"{x[0]}."
 20
 21    for i, s in enumerate( x ) :
 22        if i == 0 : 
 23            res += f"{s},"
 24        elif i < n-1 :
 25            res += f" {s},"
 26        else :
 27            res += f" {last} {s}." 
 28
 29    return res
 30
 31def assemble_word_replacement_prompt(  
 32    paragraph : str,
 33    sentence : str,
 34    alternatives : dict[ str, list[str] ]
 35) -> str : 
 36
 37    prompt = f"Given the following paragraph: {paragraph}\n\n"
 38
 39    prompt = f"Consider this sentence within the paragraph: \"{sentence}\".\n\nYour task is to rewrite the sentence by potentially replacing some particular key words with new words from a provided set of possible alternatives. Many or all of the alternatives may be mistaken, or worse choices than the existing word within the provided context. An alternative is poor if it is unsual, less precise, or if it changes the intended meaning or connotation within the given context. Be sure to ignore those poor alternatives and default to either the original word, or a better alternative, if none are correct and high quality. "
 40    
 41    for word, synonyms in alternatives.items() : 
 42        prompt += f"For the word \"{word}\", the known candidate alternatives are: " + ", ".join( synonyms ) + "."
 43    
 44    prompt += " Do not replace the words with the candidates if they are not suitable."
 45    prompt += f"Here is original sentence once more: \"{sentence}\". Output only your new replacement sentence."
 46
 47    return prompt
 48
 49def assemble_system_prompt( 
 50    core_prompt : str,
 51    core_elements : dict[ str, list[ str ] ],
 52    exposition_setting : dict[ str, list[ str ] ],
 53    np_rng,
 54    with_lists_as : Literal['inline', 'bulleted', 'spaced'] = 'inline',
 55    balance_names : bool = False,
 56    name_counts : dict[str,int] | None = None ) :
 57
 58    full_name_list = exposition_setting[ 'name suggestions' ]
 59
 60    if balance_names and name_counts is None :
 61        raise ValueError( "Balance names requires passing name counts" )
 62
 63    elif balance_names is not None  and name_counts is not None :
 64
 65        for name in full_name_list :
 66            if name not in name_counts :
 67                raise ValueError( f"Missing count for name {name}" )
 68
 69        # reqire at least 120 total names so far before we bother
 70        if sum( name_counts.values() ) >= 120 : 
 71
 72            weights = np.array( [ name_counts[ name ] for name in full_name_list ], dtype=float )
 73            weights = 1.0 / ( weights + 1 )**2
 74            weights = weights / np.sum( weights )
 75
 76            n_choose = max( int( round( len( full_name_list )*2/3 ) ) - 1, 1 )
 77
 78            full_name_list = np_rng.choice(
 79                full_name_list, 
 80                size=n_choose, 
 81                replace=False, 
 82                p=weights
 83            )
 84
 85    core_prompt = core_prompt.replace( 
 86        "NAMES",  
 87        assemble_list( np_rng.permutation( full_name_list ) ) )
 88
 89    bullets = '**' if with_lists_as == 'bulleted' else ''
 90    sep = ' ' if with_lists_as == 'inline' else '\n\n'
 91
 92    core_list = sep + sep.join( 
 93        f"{bullets}{key[0].upper()}{key[1:].lower()}{bullets}: {assemble_list( np_rng.permutation( core_elements[ key ] ) )}"
 94        for key in  np_rng.permutation( list( core_elements.keys() ) ) 
 95    )
 96
 97    core_prompt = core_prompt.replace( 'CORE', core_list )
 98
 99    return core_prompt
100
101def add_core_integrations( 
102    prompt : str,
103    vocabulary : AwVocabulary, 
104    min_k : int,
105    max_k : int,
106    min_extra_creature_integrations : int,
107    max_extra_creature_integrations : int,
108    np_rng ) -> tuple[str, list[str]]  :
109
110    integrations = vocabulary.sample_core_integrations(
111        np_rng=np_rng,
112        min_k=min_k,
113        max_k=max_k,
114        min_extra_creature_integrations=min_extra_creature_integrations,
115        max_extra_creature_integrations=max_extra_creature_integrations
116    )
117
118    extension = " Try to also incorporate the following: " + assemble_list( integrations )
119
120    return prompt + extension, integrations
121
122def add_moral_integrations( 
123    prompt : str,
124    vocabulary : AwVocabulary,
125    min_k : int,
126    max_k : int,
127    np_rng ) -> tuple[str, list[str]] :
128
129    integrations = vocabulary.sample_moral_integrations(
130        np_rng=np_rng,
131        min_k=min_k,
132        max_k=max_k
133    )
134
135    extension = " In addition, try to naturally integrate the following moral concepts: " + assemble_list( integrations )
136
137    return prompt + extension, integrations
138
139def add_underrepresented_integrations( 
140    prompt : str,
141    vocabulary : AwVocabulary,
142    min_k : int,
143    max_k : int,
144    np_rng ) -> tuple[str, list[str]] :
145    
146    # We don't boost contraction tokens or names
147    integrations = vocabulary.sample_least_gounded( min_k=min_k, max_k=max_k, np_rng=np_rng )
148    integrations = [ w for w in integrations if w and w[0].islower() and "'" not in w ]
149    
150    if not integrations :
151        return prompt, []
152
153    extension = " Be sure to also integrate the following words: " + assemble_list( integrations )
154
155    return prompt + extension, integrations
156
157def add_recursive_integrations( 
158    prompt : str,
159    previous_passages : list[str],
160    np_rng ) -> tuple[str, str] :
161
162    if not previous_passages :
163        return prompt, ""
164
165    context_content   = np_rng.choice( previous_passages, size=1 )[0]
166    context_sentences = split_sentences( context_content )
167    context_sentence  = np_rng.choice( context_sentences, size=1 )[0].strip()
168
169    extension = " In addition to narrowing your focus to thoughtful and thorough but concise incorporation of the provided integration items, also try to incorporate the concepts expressed in the following sentence: \"" + context_sentence + "\""
170
171    return prompt + extension, extension
def assemble_list(x: list[str], last: str = 'and'):
12def assemble_list( x : list[str], last : str ='and' ) :
13    
14    res = ""
15    n = len(x)
16
17    if n == 0 :
18        return res
19    elif n == 1 :
20        return f"{x[0]}."
21
22    for i, s in enumerate( x ) :
23        if i == 0 : 
24            res += f"{s},"
25        elif i < n-1 :
26            res += f" {s},"
27        else :
28            res += f" {last} {s}." 
29
30    return res
def assemble_word_replacement_prompt(paragraph: str, sentence: str, alternatives: dict[str, list[str]]) -> str:
32def assemble_word_replacement_prompt(  
33    paragraph : str,
34    sentence : str,
35    alternatives : dict[ str, list[str] ]
36) -> str : 
37
38    prompt = f"Given the following paragraph: {paragraph}\n\n"
39
40    prompt = f"Consider this sentence within the paragraph: \"{sentence}\".\n\nYour task is to rewrite the sentence by potentially replacing some particular key words with new words from a provided set of possible alternatives. Many or all of the alternatives may be mistaken, or worse choices than the existing word within the provided context. An alternative is poor if it is unsual, less precise, or if it changes the intended meaning or connotation within the given context. Be sure to ignore those poor alternatives and default to either the original word, or a better alternative, if none are correct and high quality. "
41    
42    for word, synonyms in alternatives.items() : 
43        prompt += f"For the word \"{word}\", the known candidate alternatives are: " + ", ".join( synonyms ) + "."
44    
45    prompt += " Do not replace the words with the candidates if they are not suitable."
46    prompt += f"Here is original sentence once more: \"{sentence}\". Output only your new replacement sentence."
47
48    return prompt
def assemble_system_prompt( core_prompt: str, core_elements: dict[str, list[str]], exposition_setting: dict[str, list[str]], np_rng, with_lists_as: Literal['inline', 'bulleted', 'spaced'] = 'inline', balance_names: bool = False, name_counts: dict[str, int] | None = None):
 50def assemble_system_prompt( 
 51    core_prompt : str,
 52    core_elements : dict[ str, list[ str ] ],
 53    exposition_setting : dict[ str, list[ str ] ],
 54    np_rng,
 55    with_lists_as : Literal['inline', 'bulleted', 'spaced'] = 'inline',
 56    balance_names : bool = False,
 57    name_counts : dict[str,int] | None = None ) :
 58
 59    full_name_list = exposition_setting[ 'name suggestions' ]
 60
 61    if balance_names and name_counts is None :
 62        raise ValueError( "Balance names requires passing name counts" )
 63
 64    elif balance_names is not None  and name_counts is not None :
 65
 66        for name in full_name_list :
 67            if name not in name_counts :
 68                raise ValueError( f"Missing count for name {name}" )
 69
 70        # reqire at least 120 total names so far before we bother
 71        if sum( name_counts.values() ) >= 120 : 
 72
 73            weights = np.array( [ name_counts[ name ] for name in full_name_list ], dtype=float )
 74            weights = 1.0 / ( weights + 1 )**2
 75            weights = weights / np.sum( weights )
 76
 77            n_choose = max( int( round( len( full_name_list )*2/3 ) ) - 1, 1 )
 78
 79            full_name_list = np_rng.choice(
 80                full_name_list, 
 81                size=n_choose, 
 82                replace=False, 
 83                p=weights
 84            )
 85
 86    core_prompt = core_prompt.replace( 
 87        "NAMES",  
 88        assemble_list( np_rng.permutation( full_name_list ) ) )
 89
 90    bullets = '**' if with_lists_as == 'bulleted' else ''
 91    sep = ' ' if with_lists_as == 'inline' else '\n\n'
 92
 93    core_list = sep + sep.join( 
 94        f"{bullets}{key[0].upper()}{key[1:].lower()}{bullets}: {assemble_list( np_rng.permutation( core_elements[ key ] ) )}"
 95        for key in  np_rng.permutation( list( core_elements.keys() ) ) 
 96    )
 97
 98    core_prompt = core_prompt.replace( 'CORE', core_list )
 99
100    return core_prompt
def add_core_integrations( prompt: str, vocabulary: accessible_worlds.aw_vocabulary.AwVocabulary, min_k: int, max_k: int, min_extra_creature_integrations: int, max_extra_creature_integrations: int, np_rng) -> tuple[str, list[str]]:
102def add_core_integrations( 
103    prompt : str,
104    vocabulary : AwVocabulary, 
105    min_k : int,
106    max_k : int,
107    min_extra_creature_integrations : int,
108    max_extra_creature_integrations : int,
109    np_rng ) -> tuple[str, list[str]]  :
110
111    integrations = vocabulary.sample_core_integrations(
112        np_rng=np_rng,
113        min_k=min_k,
114        max_k=max_k,
115        min_extra_creature_integrations=min_extra_creature_integrations,
116        max_extra_creature_integrations=max_extra_creature_integrations
117    )
118
119    extension = " Try to also incorporate the following: " + assemble_list( integrations )
120
121    return prompt + extension, integrations
def add_moral_integrations( prompt: str, vocabulary: accessible_worlds.aw_vocabulary.AwVocabulary, min_k: int, max_k: int, np_rng) -> tuple[str, list[str]]:
123def add_moral_integrations( 
124    prompt : str,
125    vocabulary : AwVocabulary,
126    min_k : int,
127    max_k : int,
128    np_rng ) -> tuple[str, list[str]] :
129
130    integrations = vocabulary.sample_moral_integrations(
131        np_rng=np_rng,
132        min_k=min_k,
133        max_k=max_k
134    )
135
136    extension = " In addition, try to naturally integrate the following moral concepts: " + assemble_list( integrations )
137
138    return prompt + extension, integrations
def add_underrepresented_integrations( prompt: str, vocabulary: accessible_worlds.aw_vocabulary.AwVocabulary, min_k: int, max_k: int, np_rng) -> tuple[str, list[str]]:
140def add_underrepresented_integrations( 
141    prompt : str,
142    vocabulary : AwVocabulary,
143    min_k : int,
144    max_k : int,
145    np_rng ) -> tuple[str, list[str]] :
146    
147    # We don't boost contraction tokens or names
148    integrations = vocabulary.sample_least_gounded( min_k=min_k, max_k=max_k, np_rng=np_rng )
149    integrations = [ w for w in integrations if w and w[0].islower() and "'" not in w ]
150    
151    if not integrations :
152        return prompt, []
153
154    extension = " Be sure to also integrate the following words: " + assemble_list( integrations )
155
156    return prompt + extension, integrations
def add_recursive_integrations(prompt: str, previous_passages: list[str], np_rng) -> tuple[str, str]:
158def add_recursive_integrations( 
159    prompt : str,
160    previous_passages : list[str],
161    np_rng ) -> tuple[str, str] :
162
163    if not previous_passages :
164        return prompt, ""
165
166    context_content   = np_rng.choice( previous_passages, size=1 )[0]
167    context_sentences = split_sentences( context_content )
168    context_sentence  = np_rng.choice( context_sentences, size=1 )[0].strip()
169
170    extension = " In addition to narrowing your focus to thoughtful and thorough but concise incorporation of the provided integration items, also try to incorporate the concepts expressed in the following sentence: \"" + context_sentence + "\""
171
172    return prompt + extension, extension