@promptbook/core 0.106.0-0 → 0.107.0-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/esm/index.es.js CHANGED
@@ -27,7 +27,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
27
27
  * @generated
28
28
  * @see https://github.com/webgptorg/promptbook
29
29
  */
30
- const PROMPTBOOK_ENGINE_VERSION = '0.106.0-0';
30
+ const PROMPTBOOK_ENGINE_VERSION = '0.107.0-0';
31
31
  /**
32
32
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
33
33
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -8822,115 +8822,564 @@ function $detectRuntimeEnvironment() {
8822
8822
  * TODO: [🎺] Also detect and report node version here
8823
8823
  */
8824
8824
 
8825
+ // spell-checker: disable
8825
8826
  /**
8826
- * Simple wrapper `new Date().toISOString()`
8827
- *
8828
- * Note: `$` is used to indicate that this function is not a pure function - it is not deterministic because it depends on the current time
8829
- *
8830
- * @returns string_date branded type
8831
- * @public exported from `@promptbook/utils`
8832
- */
8833
- function $getCurrentDate() {
8834
- return new Date().toISOString();
8835
- }
8836
-
8837
- /**
8838
- * Creates a human-readable hash as a short story sentence.
8827
+ * @@@
8839
8828
  *
8840
- * @public exported from `@promptbook/utils`
8841
- */
8842
- async function linguisticHash(input) {
8843
- const hash = computeHash(input);
8844
- return capitalize(createStorySentence(hash));
8845
- }
8846
- const HASH_SEGMENT_LENGTH = 8;
8847
- const STORY_OPENERS = [
8848
- 'once',
8849
- 'long ago',
8850
- 'at dawn',
8851
- 'at dusk',
8852
- 'suddenly',
8853
- 'quietly',
8854
- 'meanwhile',
8855
- 'today',
8856
- 'tonight',
8857
- 'in the end',
8858
- 'before long',
8859
- 'for a moment',
8829
+ * @private utility of `linguisticHash`
8830
+ */
8831
+ const ADJECTIVES$1 = [
8832
+ 'červený',
8833
+ 'modrý',
8834
+ 'zelený',
8835
+ 'žlutý',
8836
+ 'rychlý',
8837
+ 'pomalý',
8838
+ 'jasný',
8839
+ 'temný',
8840
+ 'veselý',
8841
+ 'smutný',
8842
+ 'statečný',
8843
+ 'klidný',
8844
+ 'chytrý',
8845
+ 'bystrý',
8846
+ 'dychtivý',
8847
+ 'honosný',
8848
+ 'velkolepý',
8849
+ 'hravý',
8850
+ 'laskavý',
8851
+ 'šťastný',
8852
+ 'hrdý',
8853
+ 'pošetilý',
8854
+ 'moudrý',
8855
+ 'mladý',
8856
+ 'starý',
8857
+ 'veliký',
8858
+ 'malý',
8859
+ 'drobný',
8860
+ 'obrovský',
8861
+ 'krátký',
8862
+ 'dlouhý',
8863
+ 'blízký',
8864
+ 'vzdálený',
8865
+ 'vnitřní',
8866
+ 'vnější',
8867
+ 'trpělivý',
8868
+ 'stálý',
8869
+ 'ušlechtilý',
8870
+ 'čistý',
8871
+ 'špinavý',
8872
+ 'čerstvý',
8873
+ 'zvětralý',
8874
+ 'ostrý',
8875
+ 'tupý',
8876
+ 'tlustý',
8877
+ 'tenký',
8878
+ 'široký',
8879
+ 'úzký',
8880
+ 'hluboký',
8881
+ 'mělký',
8882
+ 'mocný',
8883
+ 'jemný',
8884
+ 'divoký',
8885
+ 'tichý',
8886
+ 'hlučný',
8887
+ 'pokojný',
8888
+ 'rušný',
8889
+ 'prázdný',
8890
+ 'plný',
8891
+ 'kulatý',
8892
+ 'hranatý',
8893
+ 'plochý',
8894
+ 'křivý',
8895
+ 'tvrdý',
8896
+ 'měkký',
8897
+ 'teplý',
8898
+ 'chladný',
8899
+ 'sladký',
8900
+ 'kyselý',
8901
+ 'hořký',
8902
+ 'slaný',
8903
+ 'silný',
8904
+ 'slabý',
8905
+ 'pevný',
8906
+ 'pružný',
8907
+ 'křehký',
8908
+ 'houževnatý',
8909
+ 'lesklý',
8910
+ 'matný',
8911
+ 'kluzký',
8912
+ 'lepkavý',
8913
+ 'svěží',
8914
+ 'vybledlý',
8915
+ 'mlhavý',
8916
+ 'bouřlivý',
8917
+ 'slunný',
8918
+ 'větrný',
8919
+ 'deštivý',
8920
+ 'mrazivý',
8921
+ 'zlatý',
8922
+ 'stříbrný',
8923
+ 'ledový',
8924
+ 'žhavý',
8925
+ 'prastarý',
8926
+ 'moderní',
8927
+ 'skrytý',
8928
+ 'ztracený',
8929
+ 'nalezený',
8930
+ 'magický',
8931
+ 'tajemný',
8932
+ 'kosmický',
8933
+ 'hvězdný',
8934
+ 'měsíční',
8935
+ 'sluneční',
8936
+ 'mlžný',
8937
+ 'ranní',
8938
+ 'večerní',
8939
+ 'noční',
8940
+ 'denní',
8941
+ 'osamělý',
8942
+ 'společenský',
8943
+ 'soukromý',
8944
+ 'veřejný',
8945
+ 'tajný',
8946
+ 'slavný',
8947
+ 'jistý',
8948
+ 'neurčitý',
8949
+ 'prostý',
8950
+ 'snadný',
8951
+ 'krotký',
8952
+ 'mírný',
8953
+ 'horký',
8954
+ 'suchý',
8955
+ 'mokrý',
8956
+ 'vlhký',
8957
+ 'promočený',
8958
+ 'vyprahlý',
8959
+ 'hladový',
8960
+ 'žíznivý',
8961
+ 'ospalý',
8962
+ 'bdělý',
8963
+ 'unavený',
8964
+ 'líný',
8965
+ 'neklidný',
8966
+ 'nestálý',
8967
+ 'odvážný',
8968
+ 'bázlivý',
8969
+ 'upřímný',
8970
+ 'věrný',
8971
+ 'pravý',
8972
+ 'falešný',
8973
+ 'spravedlivý',
8974
+ 'jednoduchý',
8975
+ 'složitý',
8976
+ 'přirozený',
8977
+ 'umělý',
8978
+ 'živý',
8979
+ 'mrtvý',
8980
+ 'zvídavý',
8981
+ 'zvláštní',
8982
+ 'běžný',
8983
+ 'vzácný',
8984
+ 'jedinečný',
8985
+ 'základní',
8986
+ 'prvotní',
8987
+ 'hbitý',
8860
8988
  ];
8861
- const STORY_CONNECTORS = ['while', 'as', 'when', 'because', 'and', 'just as', 'after', 'before'];
8862
- const STORY_PREPOSITIONS = [
8863
- 'near',
8864
- 'beside',
8865
- 'under',
8866
- 'within',
8867
- 'beyond',
8868
- 'around',
8869
- 'behind',
8870
- 'across',
8871
- 'above',
8872
- 'beneath',
8873
- 'over',
8874
- 'inside',
8875
- 'outside',
8876
- 'along',
8989
+ const NOUNS$1 = [
8990
+ 'jablko',
8991
+ 'nebe',
8992
+ 'strom',
8993
+ 'liška',
8994
+ 'kočka',
8995
+ 'pták',
8996
+ 'pes',
8997
+ 'řeka',
8998
+ 'hora',
8999
+ 'les',
9000
+ 'oceán',
9001
+ 'hvězda',
9002
+ 'měsíc',
9003
+ 'slunce',
9004
+ 'mrak',
9005
+ 'květ',
9006
+ 'list',
9007
+ 'kámen',
9008
+ 'vítr',
9009
+ 'déšť',
9010
+ 'oheň',
9011
+ 'led',
9012
+ 'kniha',
9013
+ 'sen',
9014
+ 'píseň',
9015
+ 'cesta',
9016
+ 'brána',
9017
+ 'klíč',
9018
+ 'lampa',
9019
+ 'mapa',
9020
+ 'dům',
9021
+ 'město',
9022
+ 'most',
9023
+ 'pole',
9024
+ 'zahrada',
9025
+ 'jezero',
9026
+ 'pláž',
9027
+ 'ostrov',
9028
+ 'údolí',
9029
+ 'poušť',
9030
+ 'svět',
9031
+ 'duch',
9032
+ 'srdce',
9033
+ 'mysl',
9034
+ 'duše',
9035
+ 'život',
9036
+ 'čas',
9037
+ 'prostor',
9038
+ 'světlo',
9039
+ 'stín',
9040
+ 'zvuk',
9041
+ 'hudba',
9042
+ 'hlas',
9043
+ 'slovo',
9044
+ 'stránka',
9045
+ 'příběh',
9046
+ 'perla',
9047
+ 'zlato',
9048
+ 'stříbro',
9049
+ 'krystal',
9050
+ 'diamant',
9051
+ 'smaragd',
9052
+ 'rubín',
9053
+ 'stezka',
9054
+ 'vrchol',
9055
+ 'břeh',
9056
+ 'vlna',
9057
+ 'příliv',
9058
+ 'plamen',
9059
+ 'jiskra',
9060
+ 'paprsek',
9061
+ 'semínko',
9062
+ 'kořen',
9063
+ 'větev',
9064
+ 'pupen',
9065
+ 'trn',
9066
+ 'kůra',
9067
+ 'skořápka',
9068
+ 'pírko',
9069
+ 'křídlo',
9070
+ 'dráp',
9071
+ 'tlapa',
9072
+ 'hnízdo',
9073
+ 'jeskyně',
9074
+ 'hájek',
9075
+ 'věž',
9076
+ 'hrad',
9077
+ 'koruna',
9078
+ 'meč',
9079
+ 'štít',
9080
+ 'mince',
9081
+ 'drahokam',
9082
+ 'prsten',
9083
+ 'zvonek',
9084
+ 'hodiny',
9085
+ 'kompas',
9086
+ 'kotva',
9087
+ 'pochodeň',
9088
+ 'flétna',
9089
+ 'harfa',
9090
+ 'buben',
9091
+ 'čočka',
9092
+ 'sklo',
9093
+ 'písek',
9094
+ 'prach',
9095
+ 'mlha',
9096
+ 'rosa',
9097
+ 'svítání',
9098
+ 'soumrak',
9099
+ 'noc',
9100
+ 'den',
9101
+ 'rok',
9102
+ 'věk',
9103
+ 'blesk',
9104
+ 'kapka',
9105
+ 'bouře',
9106
+ 'sníh',
9107
+ 'kroupa',
9108
+ 'kouř',
9109
+ 'pára',
9110
+ 'plyn',
9111
+ 'kov',
9112
+ 'skála',
9113
+ 'hlína',
9114
+ 'sůl',
9115
+ 'cukr',
9116
+ 'dřevo',
9117
+ 'kost',
9118
+ 'kůže',
9119
+ 'tělo',
9120
+ 'krev',
9121
+ 'buňka',
9122
+ 'atom',
9123
+ 'tep',
9124
+ 'dech',
9125
+ 'vzdech',
9126
+ 'jméno',
9127
+ 'ozvěna',
9128
+ 'obraz',
9129
+ 'vize',
9130
+ 'myšlenka',
9131
+ 'nápad',
9132
+ 'plán',
9133
+ 'cíl',
9134
+ 'přání',
9135
+ 'naděje',
9136
+ 'strach',
9137
+ 'radost',
9138
+ 'láska',
9139
+ 'nenávist',
9140
+ 'vůle',
9141
+ 'síla',
9142
+ 'energie',
9143
+ 'pohyb',
9144
+ 'rychlost',
9145
+ 'místo',
9146
+ 'bod',
9147
+ 'linie',
9148
+ 'tvar',
9149
+ 'forma',
9150
+ 'velikost',
9151
+ 'hmota',
9152
+ 'váha',
9153
+ 'teplo',
9154
+ 'chlad',
9155
+ 'barva',
9156
+ 'tón',
9157
+ 'rytmus',
9158
+ 'nálada',
9159
+ 'stav',
9160
+ 'krok',
9161
+ 'pád',
9162
+ 'skok',
9163
+ 'běh',
9164
+ 'let',
9165
+ 'klid',
9166
+ 'úkol',
9167
+ 'práce',
9168
+ 'hra',
9169
+ 'sport',
9170
+ 'umění',
9171
+ 'řemeslo',
9172
+ 'nástroj',
9173
+ 'loď',
9174
+ 'člun',
9175
+ 'auto',
9176
+ 'kolo',
9177
+ 'vlak',
9178
+ 'letadlo',
9179
+ 'uzel',
9180
+ 'síť',
9181
+ 'krabice',
9182
+ 'taška',
9183
+ 'dóza',
9184
+ 'hrnek',
9185
+ 'miska',
9186
+ 'talíř',
9187
+ 'lžíce',
9188
+ 'vidlička',
9189
+ 'nůž',
9190
+ 'pánev',
9191
+ 'hrnec',
9192
+ 'postel',
9193
+ 'stůl',
9194
+ 'židle',
9195
+ 'dveře',
9196
+ 'stěna',
9197
+ 'střecha',
9198
+ 'podlaha',
9199
+ 'okno',
9200
+ 'chodba',
8877
9201
  ];
8878
- const STORY_TEMPLATES = [
8879
- (parts) => `${parts.opener}, the ${parts.adjective1} ${parts.noun1} was ${parts.verb1} ${parts.connector} the ${parts.adjective2} ${parts.noun2} was ${parts.verb2} ${parts.preposition} the ${parts.adjective3} ${parts.noun3}.`,
8880
- (parts) => `${parts.opener}, the ${parts.adjective1} ${parts.noun1} was ${parts.verb1} ${parts.preposition} the ${parts.adjective2} ${parts.noun2}, and the ${parts.adjective3} ${parts.noun3} was ${parts.verb2}.`,
8881
- (parts) => `${parts.opener}, the ${parts.adjective1} ${parts.noun1} was ${parts.verb1}, and the ${parts.adjective2} ${parts.noun2} was ${parts.verb2} ${parts.preposition} the ${parts.adjective3} ${parts.noun3}.`,
8882
- (parts) => `${parts.opener}, the ${parts.adjective1} ${parts.noun1} was ${parts.verb1} ${parts.connector} the ${parts.adjective2} ${parts.noun2} was ${parts.verb2}, ${parts.preposition} the ${parts.adjective3} ${parts.noun3}.`,
9202
+ const VERBS$1 = [
9203
+ 'skáče',
9204
+ 'tančí',
9205
+ 'letí',
9206
+ 'běží',
9207
+ 'zpívá',
9208
+ 'svítí',
9209
+ 'roste',
9210
+ 'plyne',
9211
+ 'padá',
9212
+ 'stoupá',
9213
+ 'spí',
9214
+ 'kráčí',
9215
+ 'mluví',
9216
+ 'myslí',
9217
+ 'sní',
9218
+ 'hledá',
9219
+ 'nachází',
9220
+ 'dává',
9221
+ 'bere',
9222
+ 'tvoří',
9223
+ 'hoří',
9224
+ 'mrzne',
9225
+ 'taje',
9226
+ 'dýchá',
9227
+ 'pulzuje',
9228
+ 'bije',
9229
+ 'žije',
9230
+ 'učí',
9231
+ 'ví',
9232
+ 'skrývá',
9233
+ 'ukazuje',
9234
+ 'láme',
9235
+ 'opravuje',
9236
+ 'ztrácí',
9237
+ 'nalézá',
9238
+ 'začíná',
9239
+ 'končí',
9240
+ 'plave',
9241
+ 'pluje',
9242
+ 'klouže',
9243
+ 'točí',
9244
+ 'mění',
9245
+ 'bledne',
9246
+ 'mizí',
9247
+ 'rodí',
9248
+ 'hučí',
9249
+ 'pláče',
9250
+ 'závodí',
9251
+ 'plíží',
9252
+ 'sleduje',
9253
+ 'slyší',
9254
+ 'cítí',
9255
+ 'touží',
9256
+ 'doufá',
9257
+ 'miluje',
9258
+ 'bloudí',
9259
+ 'putuje',
9260
+ 'cestuje',
9261
+ 'překračuje',
9262
+ 'potkává',
9263
+ 'drží',
9264
+ 'sdílí',
9265
+ 'jiskří',
9266
+ 'plápolá',
9267
+ 'léčí',
9268
+ 'řeší',
9269
+ 'otevírá',
9270
+ 'zavírá',
9271
+ 'zvedá',
9272
+ 'táhne',
9273
+ 'tlačí',
9274
+ 'hází',
9275
+ 'chytá',
9276
+ 'dělá',
9277
+ 'vidí',
9278
+ 'chutná',
9279
+ 'voní',
9280
+ 'spěchá',
9281
+ 'zastaví',
9282
+ 'jde',
9283
+ 'přichází',
9284
+ 'odchází',
9285
+ 'jedná',
9286
+ 'existuje',
9287
+ 'zmenšuje',
9288
+ 'rozšiřuje',
9289
+ 'zužuje',
9290
+ 'hřeje',
9291
+ 'chladí',
9292
+ 'suší',
9293
+ 'máčí',
9294
+ 'plní',
9295
+ 'vyprazdňuje',
9296
+ 'pouští',
9297
+ 'získává',
9298
+ 'vítězí',
9299
+ 'selhává',
9300
+ 'zkouší',
9301
+ 'používá',
9302
+ 'dostává',
9303
+ 'měří',
9304
+ 'stojí',
9305
+ 'dosahuje',
9306
+ 'míjí',
9307
+ 'udeří',
9308
+ 'vede',
9309
+ 'následuje',
9310
+ 'pomáhá',
9311
+ 'slouží',
9312
+ 'trénuje',
9313
+ 'kóduje',
9314
+ 'píše',
9315
+ 'čte',
9316
+ 'kreslí',
9317
+ 'maluje',
9318
+ 'tvaruje',
9319
+ 'spojuje',
9320
+ 'dělí',
9321
+ 'váže',
9322
+ 'zraňuje',
9323
+ 'chrání',
9324
+ 'bojuje',
9325
+ 'brání',
9326
+ 'útočí',
9327
+ 'uniká',
9328
+ 'lapá',
9329
+ 'osvobozuje',
9330
+ 'poutá',
9331
+ 'spřádá',
9332
+ 'tká',
9333
+ 'vrhá',
9334
+ 'nese',
9335
+ 'přenáší',
9336
+ 'vrací',
9337
+ 'zrychluje',
9338
+ 'zpomaluje',
9339
+ 'probouzí',
9340
+ 'uspává',
9341
+ 'šeptá',
9342
+ 'volá',
9343
+ 'hledí',
9344
+ 'čeká',
9345
+ 'bdí',
9346
+ 'rozkvétá',
9347
+ 'klíčí',
9348
+ 'zraje',
9349
+ 'chvěje',
9350
+ 'třpytí',
9351
+ 'shromažďuje',
9352
+ 'rozhazuje',
9353
+ 'tápe',
9354
+ 'žhne',
9355
+ 'vibruje',
9356
+ 'šumí',
9357
+ 'stéká',
9358
+ 'vypráví',
9359
+ 'plánuje',
9360
+ 'počítá',
9361
+ 'váhá',
9362
+ 'riskuje',
8883
9363
  ];
8884
9364
  /**
8885
- * Extracts a deterministic numeric seed from a SHA-256 hash.
8886
- */
8887
- function getHashSeed(hash, segmentIndex) {
8888
- const expandedHash = `${hash}${hash}`;
8889
- const start = (segmentIndex * HASH_SEGMENT_LENGTH + segmentIndex) % hash.length;
8890
- return parseInt(expandedHash.substring(start, start + HASH_SEGMENT_LENGTH), 16);
8891
- }
8892
- /**
8893
- * Picks a deterministic item from a list based on the hash seed.
8894
- */
8895
- function pickFromHash(hash, segmentIndex, list) {
8896
- const seed = getHashSeed(hash, segmentIndex);
8897
- return list[seed % list.length];
8898
- }
8899
- /**
8900
- * Index constants for story part selection to avoid magic numbers.
8901
- */
8902
- const ADJECTIVE3_INDEX = 9;
8903
- const NOUN3_INDEX = 10;
8904
- /**
8905
- * Creates the deterministic story parts used by the sentence templates.
9365
+ * Czech word lists used by the linguistic hash.
9366
+ *
9367
+ * @private utility of `linguisticHash`
8906
9368
  */
8907
- function createStoryParts(hash) {
8908
- return {
8909
- opener: pickFromHash(hash, 0, STORY_OPENERS),
8910
- connector: pickFromHash(hash, 1, STORY_CONNECTORS),
8911
- preposition: pickFromHash(hash, 2, STORY_PREPOSITIONS),
8912
- adjective1: pickFromHash(hash, 3, ADJECTIVES),
8913
- noun1: pickFromHash(hash, 4, NOUNS),
8914
- verb1: pickFromHash(hash, 5, VERBS),
8915
- adjective2: pickFromHash(hash, 6, ADJECTIVES),
8916
- noun2: pickFromHash(hash, 7, NOUNS),
8917
- verb2: pickFromHash(hash, 8, VERBS),
8918
- adjective3: pickFromHash(hash, ADJECTIVE3_INDEX, ADJECTIVES),
8919
- noun3: pickFromHash(hash, NOUN3_INDEX, NOUNS),
8920
- };
8921
- }
9369
+ const LINGUISTIC_HASH_WORD_LISTS_CS = {
9370
+ adjective: ADJECTIVES$1,
9371
+ noun: NOUNS$1,
9372
+ verb: VERBS$1,
9373
+ };
8922
9374
  /**
8923
- * Index constant for story template selection to avoid magic numbers.
9375
+ * Note: [💞] Ignore a discrepancy between file name and entity name
8924
9376
  */
8925
- const STORY_TEMPLATE_INDEX = 11;
9377
+
8926
9378
  /**
8927
- * Builds a short, memorable story sentence from the hash.
9379
+ * @@@
9380
+ *
9381
+ * @private utility of `linguisticHash`
8928
9382
  */
8929
- function createStorySentence(hash) {
8930
- const parts = createStoryParts(hash);
8931
- const template = pickFromHash(hash, STORY_TEMPLATE_INDEX, STORY_TEMPLATES);
8932
- return template(parts).trim();
8933
- }
8934
9383
  const ADJECTIVES = [
8935
9384
  'red',
8936
9385
  'blue',
@@ -9605,8 +10054,215 @@ const VERBS = [
9605
10054
  'spinning',
9606
10055
  ];
9607
10056
  /**
9608
- * TODO: Prompt: Extract number constants and word list to a separate file for reuse.
10057
+ * English word lists used by the linguistic hash.
10058
+ *
10059
+ * @private utility of `linguisticHash`
10060
+ */
10061
+ const LINGUISTIC_HASH_WORD_LISTS_EN = {
10062
+ adjective: ADJECTIVES,
10063
+ noun: NOUNS,
10064
+ verb: VERBS,
10065
+ };
10066
+ /**
10067
+ * Note: [💞] Ignore a discrepancy between file name and entity name
10068
+ */
10069
+
10070
+ /**
10071
+ * Default language used for linguistic hashes.
10072
+ *
10073
+ * @private utility of `linguisticHash`
10074
+ */
10075
+ const DEFAULT_LINGUISTIC_HASH_LANGUAGE = 'en';
10076
+ /**
10077
+ * @@@
10078
+ *
10079
+ * @private utility of `linguisticHash`
10080
+ */
10081
+ const LANGUAGE_CONFIGS = {
10082
+ en: {
10083
+ language: 'en',
10084
+ label: 'English',
10085
+ wordLists: LINGUISTIC_HASH_WORD_LISTS_EN,
10086
+ },
10087
+ cs: {
10088
+ language: 'cs',
10089
+ label: 'Czech',
10090
+ wordLists: LINGUISTIC_HASH_WORD_LISTS_CS,
10091
+ },
10092
+ };
10093
+ /**
10094
+ * Normalizes a requested language to a supported linguistic hash language.
10095
+ *
10096
+ * @private utility of `linguisticHash`
10097
+ */
10098
+ function normalizeLinguisticHashLanguage(language) {
10099
+ if (typeof language !== 'string') {
10100
+ return DEFAULT_LINGUISTIC_HASH_LANGUAGE;
10101
+ }
10102
+ const normalized = language.trim().toLowerCase();
10103
+ if (normalized === 'cs') {
10104
+ return 'cs';
10105
+ }
10106
+ if (normalized === 'en') {
10107
+ return 'en';
10108
+ }
10109
+ return DEFAULT_LINGUISTIC_HASH_LANGUAGE;
10110
+ }
10111
+ /**
10112
+ * Returns the language configuration for linguistic hash generation.
10113
+ *
10114
+ * @private utility of `linguisticHash`
10115
+ */
10116
+ function getLinguisticHashLanguageConfig(language) {
10117
+ const normalized = normalizeLinguisticHashLanguage(language);
10118
+ return LANGUAGE_CONFIGS[normalized];
10119
+ }
10120
+
10121
+ // <- TODO: !!!! Remove re-exports
10122
+ /**
10123
+ * Creates a human-readable hash as a short, story-like phrase.
10124
+ *
10125
+ * @param wordCount how many words to include (defaults to {@link DEFAULT_LINGUISTIC_HASH_WORD_COUNT}, clamped to
10126
+ * {@link MIN_LINGUISTIC_HASH_WORD_COUNT}..{@link MAX_LINGUISTIC_HASH_WORD_COUNT})
10127
+ * @param language optional language code (defaults to {@link DEFAULT_LINGUISTIC_HASH_LANGUAGE})
10128
+ *
10129
+ * @public exported from `@promptbook/utils`
10130
+ */
10131
+ async function linguisticHash(input, wordCount, language) {
10132
+ const hash = computeHash(input);
10133
+ const normalizedWordCount = normalizeLinguisticHashWordCount(wordCount);
10134
+ const languageConfig = getLinguisticHashLanguageConfig(language);
10135
+ const words = createLinguisticHashWords(hash, normalizedWordCount, languageConfig.wordLists);
10136
+ return capitalize(words.join(' '));
10137
+ }
10138
+ /**
10139
+ * @@@
10140
+ *
10141
+ * @private utility of `linguisticHash`
10142
+ */
10143
+ const HASH_SEGMENT_LENGTH = 8;
10144
+ /**
10145
+ * The minimum number of words for a linguistic hash.
10146
+ *
10147
+ * @private utility of `linguisticHash`
10148
+ */
10149
+ const MIN_LINGUISTIC_HASH_WORD_COUNT = 1;
10150
+ /**
10151
+ * The default number of words for a linguistic hash.
10152
+ *
10153
+ * @private utility of `linguisticHash`
10154
+ */
10155
+ const DEFAULT_LINGUISTIC_HASH_WORD_COUNT = 7;
10156
+ /**
10157
+ * Extracts a deterministic numeric seed from a SHA-256 hash.
10158
+ *
10159
+ * @private utility of `linguisticHash`
10160
+ */
10161
+ function getHashSeed(hash, segmentIndex) {
10162
+ const expandedHash = `${hash}${hash}`;
10163
+ const start = (segmentIndex * HASH_SEGMENT_LENGTH + segmentIndex) % hash.length;
10164
+ return parseInt(expandedHash.substring(start, start + HASH_SEGMENT_LENGTH), 16);
10165
+ }
10166
+ /**
10167
+ * Picks a deterministic item from a list based on the hash seed.
10168
+ *
10169
+ * @private utility of `linguisticHash`
10170
+ */
10171
+ function pickFromHash(hash, segmentIndex, list) {
10172
+ const seed = getHashSeed(hash, segmentIndex);
10173
+ return list[seed % list.length];
10174
+ }
10175
+ /**
10176
+ * Ordered word kinds used to build the linguistic hash output.
10177
+ *
10178
+ * @private utility of `linguisticHash`
10179
+ */
10180
+ const WORD_SEQUENCE = [
10181
+ 'adjective',
10182
+ 'noun',
10183
+ 'verb',
10184
+ 'adjective',
10185
+ 'noun',
10186
+ 'verb',
10187
+ 'adjective',
10188
+ 'noun',
10189
+ 'verb',
10190
+ 'adjective',
10191
+ 'noun',
10192
+ 'verb',
10193
+ 'adjective',
10194
+ 'noun',
10195
+ 'verb',
10196
+ 'adjective',
10197
+ 'noun',
10198
+ 'verb',
10199
+ 'adjective',
10200
+ 'noun',
10201
+ ];
10202
+ /**
10203
+ * The maximum number of words for a linguistic hash.
10204
+ *
10205
+ * @private utility of `linguisticHash`
10206
+ */
10207
+ const MAX_LINGUISTIC_HASH_WORD_COUNT = WORD_SEQUENCE.length;
10208
+ /**
10209
+ * Index of the noun used for single-word hashes.
10210
+ *
10211
+ * @private utility of `linguisticHash`
10212
+ */
10213
+ const SINGLE_WORD_INDEX = 1;
10214
+ /**
10215
+ * Normalizes the word count to a supported integer range.
10216
+ *
10217
+ * @private utility of `linguisticHash`
10218
+ */
10219
+ function normalizeLinguisticHashWordCount(wordCount) {
10220
+ if (typeof wordCount !== 'number' || !Number.isFinite(wordCount)) {
10221
+ return DEFAULT_LINGUISTIC_HASH_WORD_COUNT;
10222
+ }
10223
+ const rounded = Math.round(wordCount);
10224
+ return Math.min(MAX_LINGUISTIC_HASH_WORD_COUNT, Math.max(MIN_LINGUISTIC_HASH_WORD_COUNT, rounded));
10225
+ }
10226
+ /**
10227
+ * Picks a deterministic word from the hash by kind.
10228
+ *
10229
+ * @private utility of `linguisticHash`
10230
+ */
10231
+ function pickWordFromHash(hash, segmentIndex, wordKind, wordLists) {
10232
+ return pickFromHash(hash, segmentIndex, wordLists[wordKind]);
10233
+ }
10234
+ /**
10235
+ * Creates the deterministic word sequence used for the linguistic hash output.
10236
+ *
10237
+ * @private utility of `linguisticHash`
10238
+ */
10239
+ function createLinguisticHashWordSequence(hash, wordLists) {
10240
+ return WORD_SEQUENCE.map((wordKind, index) => pickWordFromHash(hash, index, wordKind, wordLists));
10241
+ }
10242
+ /**
10243
+ * Selects the requested number of words from the hash output.
10244
+ *
10245
+ * @private utility of `linguisticHash`
10246
+ */
10247
+ function createLinguisticHashWords(hash, wordCount, wordLists) {
10248
+ const words = createLinguisticHashWordSequence(hash, wordLists);
10249
+ if (wordCount === 1) {
10250
+ return [words[SINGLE_WORD_INDEX]];
10251
+ }
10252
+ return words.slice(0, wordCount);
10253
+ }
10254
+
10255
+ /**
10256
+ * Simple wrapper `new Date().toISOString()`
10257
+ *
10258
+ * Note: `$` is used to indicate that this function is not a pure function - it is not deterministic because it depends on the current time
10259
+ *
10260
+ * @returns string_date branded type
10261
+ * @public exported from `@promptbook/utils`
9609
10262
  */
10263
+ function $getCurrentDate() {
10264
+ return new Date().toISOString();
10265
+ }
9610
10266
 
9611
10267
  /**
9612
10268
  * Function parseNumber will parse number from string
@@ -20265,6 +20921,21 @@ function asUpdatableSubject(value) {
20265
20921
  * TODO: [🧠] Maybe `BehaviorSubject` is too heavy for this use case, maybe just tuple `[value,setValue]` is enough
20266
20922
  */
20267
20923
 
20924
+ /**
20925
+ * Tool call name emitted while preparing a GPT assistant for an agent.
20926
+ *
20927
+ * @public exported from `@promptbook/types`
20928
+ */
20929
+ const ASSISTANT_PREPARATION_TOOL_CALL_NAME = 'assistant_preparation';
20930
+ /**
20931
+ * Checks whether a tool call is the assistant preparation marker.
20932
+ *
20933
+ * @public exported from `@promptbook/types`
20934
+ */
20935
+ function isAssistantPreparationToolCall(toolCall) {
20936
+ return toolCall.name === ASSISTANT_PREPARATION_TOOL_CALL_NAME;
20937
+ }
20938
+
20268
20939
  /**
20269
20940
  * Change ellipsis characters and dot leaders to three dots `…` -> `...`
20270
20941
  *
@@ -23072,6 +23743,33 @@ const DISCRIMINANT = 'OPEN_AI_ASSISTANT_V1';
23072
23743
  * TODO: [🧠][🌰] Allow to pass `title` for tracking purposes
23073
23744
  */
23074
23745
 
23746
+ /**
23747
+ * Emits a progress update to signal assistant preparation before long setup work.
23748
+ */
23749
+ function emitAssistantPreparationProgress(options) {
23750
+ const startedAt = $getCurrentDate();
23751
+ options.onProgress({
23752
+ content: '',
23753
+ modelName: options.modelName,
23754
+ timing: {
23755
+ start: startedAt,
23756
+ complete: startedAt,
23757
+ },
23758
+ usage: UNCERTAIN_USAGE,
23759
+ rawPromptContent: options.prompt.content,
23760
+ rawRequest: null,
23761
+ rawResponse: {
23762
+ status: 'assistant_preparation',
23763
+ },
23764
+ toolCalls: [
23765
+ {
23766
+ name: ASSISTANT_PREPARATION_TOOL_CALL_NAME,
23767
+ arguments: {},
23768
+ createdAt: startedAt,
23769
+ },
23770
+ ],
23771
+ });
23772
+ }
23075
23773
  /**
23076
23774
  * Execution Tools for calling LLM models with a predefined agent "soul"
23077
23775
  * This wraps underlying LLM execution tools and applies agent-specific system prompts and requirements
@@ -23295,6 +23993,11 @@ class AgentLlmExecutionTools {
23295
23993
  if (this.options.isVerbose) {
23296
23994
  console.log(`1️⃣ Updating OpenAI Assistant for agent ${this.title}...`);
23297
23995
  }
23996
+ emitAssistantPreparationProgress({
23997
+ onProgress,
23998
+ prompt,
23999
+ modelName: this.modelName,
24000
+ });
23298
24001
  assistant = await this.options.llmTools.updateAssistant({
23299
24002
  assistantId: cached.assistantId,
23300
24003
  name: this.title,
@@ -23313,6 +24016,11 @@ class AgentLlmExecutionTools {
23313
24016
  console.log(`1️⃣ Creating new OpenAI Assistant for agent ${this.title}...`);
23314
24017
  }
23315
24018
  // <- TODO: [🐱‍🚀] Check also `isCreatingNewAssistantsAllowed` and warn about it
24019
+ emitAssistantPreparationProgress({
24020
+ onProgress,
24021
+ prompt,
24022
+ modelName: this.modelName,
24023
+ });
23316
24024
  assistant = await this.options.llmTools.createNewAssistant({
23317
24025
  name: this.title,
23318
24026
  instructions: modelRequirements.systemMessage,
@@ -23967,6 +24675,8 @@ class RemoteAgent extends Agent {
23967
24675
  // <- TODO: [🐱‍🚀] Maybe use promptbookFetch
23968
24676
  let content = '';
23969
24677
  const toolCalls = [];
24678
+ const preparationToolCalls = [];
24679
+ let hasReceivedModelOutput = false;
23970
24680
  const normalizeToolCall = (toolCall) => {
23971
24681
  if (toolCall.createdAt) {
23972
24682
  return toolCall;
@@ -24013,6 +24723,17 @@ class RemoteAgent extends Agent {
24013
24723
  const upsertToolCalls = (incomingToolCalls) => {
24014
24724
  for (const toolCall of incomingToolCalls) {
24015
24725
  const normalized = normalizeToolCall(toolCall);
24726
+ if (isAssistantPreparationToolCall(normalized)) {
24727
+ if (hasReceivedModelOutput) {
24728
+ continue;
24729
+ }
24730
+ preparationToolCalls.length = 0;
24731
+ preparationToolCalls.push(normalized);
24732
+ continue;
24733
+ }
24734
+ if (preparationToolCalls.length > 0) {
24735
+ preparationToolCalls.length = 0;
24736
+ }
24016
24737
  const key = getToolCallKey(normalized);
24017
24738
  const existingIndex = toolCalls.findIndex((existing) => getToolCallKey(existing) === key);
24018
24739
  if (existingIndex === -1) {
@@ -24023,6 +24744,10 @@ class RemoteAgent extends Agent {
24023
24744
  }
24024
24745
  }
24025
24746
  };
24747
+ /**
24748
+ * Builds the tool call list including any preparation marker still active.
24749
+ */
24750
+ const getActiveToolCalls = () => preparationToolCalls.length > 0 ? [...preparationToolCalls, ...toolCalls] : toolCalls;
24026
24751
  if (!bookResponse.body) {
24027
24752
  content = await bookResponse.text();
24028
24753
  }
@@ -24059,7 +24784,7 @@ class RemoteAgent extends Agent {
24059
24784
  rawPromptContent: {},
24060
24785
  rawRequest: {},
24061
24786
  rawResponse: {},
24062
- toolCalls: normalizedToolCalls,
24787
+ toolCalls: getActiveToolCalls(),
24063
24788
  });
24064
24789
  sawToolCalls = true;
24065
24790
  isToolCallLine = true;
@@ -24087,6 +24812,10 @@ class RemoteAgent extends Agent {
24087
24812
  // console.debug('RemoteAgent chunk:', textChunk);
24088
24813
  content += textChunk;
24089
24814
  }
24815
+ if (!hasReceivedModelOutput && content.trim().length > 0) {
24816
+ hasReceivedModelOutput = true;
24817
+ preparationToolCalls.length = 0;
24818
+ }
24090
24819
  onProgress({
24091
24820
  content,
24092
24821
  modelName: this.modelName,
@@ -24095,7 +24824,7 @@ class RemoteAgent extends Agent {
24095
24824
  rawPromptContent: {},
24096
24825
  rawRequest: {},
24097
24826
  rawResponse: {},
24098
- toolCalls,
24827
+ toolCalls: getActiveToolCalls(),
24099
24828
  });
24100
24829
  }
24101
24830
  }
@@ -24103,6 +24832,10 @@ class RemoteAgent extends Agent {
24103
24832
  const lastChunk = decoder.decode();
24104
24833
  if (lastChunk) {
24105
24834
  content += lastChunk;
24835
+ if (!hasReceivedModelOutput && content.trim().length > 0) {
24836
+ hasReceivedModelOutput = true;
24837
+ preparationToolCalls.length = 0;
24838
+ }
24106
24839
  onProgress({
24107
24840
  content: lastChunk,
24108
24841
  modelName: this.modelName,
@@ -24111,7 +24844,7 @@ class RemoteAgent extends Agent {
24111
24844
  rawPromptContent: {},
24112
24845
  rawRequest: {},
24113
24846
  rawResponse: {},
24114
- toolCalls,
24847
+ toolCalls: getActiveToolCalls(),
24115
24848
  });
24116
24849
  }
24117
24850
  }