Compare commits
2 Commits
4583f6dac4
...
10f6de2ee5
Author | SHA1 | Date |
---|---|---|
projectmoon | 10f6de2ee5 | |
projectmoon | 0995a0c26d |
|
@ -35,49 +35,6 @@ fn is_direction(value: &str) -> bool {
|
|||
DIRECTIONS.contains(&value.to_lowercase().as_ref())
|
||||
}
|
||||
|
||||
/// If LLM generates something odd, reject it.
|
||||
fn is_weird_exit_name(value: &str) -> bool {
|
||||
value.to_lowercase().contains("connected scene")
|
||||
|| value.to_lowercase() == root_scene_id().as_ref()
|
||||
}
|
||||
|
||||
fn is_duplicate_recorded(failures: &[CoherenceFailure], exit: &Exit) -> bool {
|
||||
for failure in failures {
|
||||
match failure {
|
||||
CoherenceFailure::DuplicateExits(exits) => {
|
||||
if exits.iter().find(|e| e.name == exit.name).is_some() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub(super) fn check_scene_coherence<'a>(scene: &'a Scene) -> Vec<CoherenceFailure<'a>> {
|
||||
let mut failures: Vec<CoherenceFailure> = vec![];
|
||||
|
||||
for exit in scene.exits.as_slice() {
|
||||
// Exit names cannot be directions, "weird", or the name of
|
||||
// the current scene itself.
|
||||
if is_direction(&exit.name) || is_weird_exit_name(&exit.name) || exit.name == scene.name {
|
||||
failures.push(CoherenceFailure::InvalidExitName(exit));
|
||||
}
|
||||
|
||||
// Also need to detect duplicate exits by direction. Stub
|
||||
// creation can have two exits that lead the same way.
|
||||
let duplicate_exits: Vec<_> = scene.exits.iter().filter(|e| e.name == exit.name).collect();
|
||||
|
||||
if duplicate_exits.len() > 1 && !is_duplicate_recorded(&failures, exit) {
|
||||
failures.push(CoherenceFailure::DuplicateExits(duplicate_exits));
|
||||
}
|
||||
}
|
||||
|
||||
failures
|
||||
}
|
||||
|
||||
pub fn reverse_direction(direction: &str) -> String {
|
||||
match direction.to_lowercase().as_ref() {
|
||||
// compass directions
|
||||
|
@ -101,100 +58,156 @@ pub fn reverse_direction(direction: &str) -> String {
|
|||
}
|
||||
}
|
||||
|
||||
/// Attempt to reconnect back to the connected scene. The model is not
|
||||
/// always good at this. Here, we correct it by attempting to find the
|
||||
/// exit and making sure the direction is coherently reversed. A
|
||||
/// linkback exit is created from scratch if one cannot be found.
|
||||
pub fn make_scene_from_stub_coherent(content: &mut ContentContainer, connected_scene: &Scene) {
|
||||
let new_scene = content.owner.as_scene_mut();
|
||||
let connected_key = connected_scene._key.as_deref().unwrap();
|
||||
let connected_id = connected_scene._id.as_deref().unwrap();
|
||||
|
||||
let direction_from = connected_scene
|
||||
.exits
|
||||
.iter()
|
||||
.find(|exit| &exit.scene_key == new_scene._key.as_ref().unwrap())
|
||||
.map(|exit| exit.direction.as_ref())
|
||||
.unwrap_or("from");
|
||||
|
||||
let reversed_direction = reverse_direction(direction_from);
|
||||
|
||||
// 1. Delete any exit that is from the reversed direction, or
|
||||
// has the name/ID/key of the connected scene.
|
||||
let mut stubs_to_delete = vec![];
|
||||
let keep_exit = |exit: &Exit| {
|
||||
!(exit.direction == reversed_direction
|
||||
|| Some(exit.scene_key.as_ref()) == connected_scene._key.as_deref()
|
||||
|| exit.scene_id.as_deref() == connected_scene._id.as_deref()
|
||||
|| exit.name.to_lowercase() == connected_scene.name.to_lowercase()
|
||||
|| exit.name == connected_key
|
||||
|| exit.name == connected_id)
|
||||
};
|
||||
|
||||
new_scene.exits.retain_mut(|exit| {
|
||||
let keep = keep_exit(exit);
|
||||
if !keep {
|
||||
stubs_to_delete.push(mem::take(&mut exit.scene_key));
|
||||
}
|
||||
keep
|
||||
});
|
||||
|
||||
// 2. Delete corresponding scene stubs
|
||||
content.contained.retain(|c| match &c.content {
|
||||
Content::SceneStub(stub) => match stub._key.as_ref() {
|
||||
Some(key) => !stubs_to_delete.contains(key),
|
||||
_ => true,
|
||||
},
|
||||
_ => true,
|
||||
});
|
||||
|
||||
// 3. Add new linkback exit
|
||||
let exit = Exit::from_connected_scene(connected_scene, &reversed_direction);
|
||||
new_scene.exits.push(exit);
|
||||
/// If LLM generates something odd, reject it.
|
||||
fn is_weird_exit_name(value: &str) -> bool {
|
||||
value.to_lowercase().contains("connected scene")
|
||||
|| value.to_lowercase() == root_scene_id().as_ref()
|
||||
}
|
||||
|
||||
pub async fn make_scene_coherent(
|
||||
generator: &mut AiClient,
|
||||
content: &mut ContentContainer,
|
||||
) -> Result<()> {
|
||||
let scene = content.owner.as_scene_mut();
|
||||
let failures = check_scene_coherence(&scene);
|
||||
let fixes = generator.fix_scene(&scene, failures).await?;
|
||||
let mut deletes = vec![]; // Needed for call to Vec::retain after the fact
|
||||
|
||||
for fix in fixes {
|
||||
match fix {
|
||||
SceneFix::FixedExit {
|
||||
index,
|
||||
new: fixed_exit,
|
||||
} => {
|
||||
let old_exit_key = scene.exits[index].scene_key.as_str();
|
||||
|
||||
content.contained.retain(|c| match &c.content {
|
||||
Content::SceneStub(stub) => stub._key.as_deref() != Some(old_exit_key),
|
||||
_ => true,
|
||||
});
|
||||
|
||||
scene.exits[index] = fixed_exit.into();
|
||||
let fixed_exit = &scene.exits[index];
|
||||
|
||||
content
|
||||
.contained
|
||||
.push(ContentRelation::scene_stub(SceneStub::from(fixed_exit)));
|
||||
fn is_duplicate_recorded(failures: &[CoherenceFailure], exit: &Exit) -> bool {
|
||||
for failure in failures {
|
||||
match failure {
|
||||
CoherenceFailure::DuplicateExits(exits) => {
|
||||
if exits.iter().find(|e| e.name == exit.name).is_some() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
SceneFix::DeleteExit(index) => {
|
||||
deletes.push(index);
|
||||
}
|
||||
};
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
// Deletes
|
||||
let mut index: usize = 0;
|
||||
scene.exits.retain(|_| {
|
||||
let keep_it = !deletes.contains(&index);
|
||||
index += 1;
|
||||
keep_it
|
||||
});
|
||||
|
||||
Ok(())
|
||||
false
|
||||
}
|
||||
|
||||
pub(super) struct AiCoherence {
|
||||
generator: Rc<AiClient>,
|
||||
}
|
||||
|
||||
impl AiCoherence {
|
||||
pub fn new(generator: Rc<AiClient>) -> AiCoherence {
|
||||
AiCoherence { generator }
|
||||
}
|
||||
|
||||
fn check_scene_coherence<'a>(&self, scene: &'a Scene) -> Vec<CoherenceFailure<'a>> {
|
||||
let mut failures: Vec<CoherenceFailure> = vec![];
|
||||
|
||||
for exit in scene.exits.as_slice() {
|
||||
// Exit names cannot be directions, "weird", or the name of
|
||||
// the current scene itself.
|
||||
if is_direction(&exit.name) || is_weird_exit_name(&exit.name) || exit.name == scene.name
|
||||
{
|
||||
failures.push(CoherenceFailure::InvalidExitName(exit));
|
||||
}
|
||||
|
||||
// Also need to detect duplicate exits by direction. Stub
|
||||
// creation can have two exits that lead the same way.
|
||||
let duplicate_exits: Vec<_> =
|
||||
scene.exits.iter().filter(|e| e.name == exit.name).collect();
|
||||
|
||||
if duplicate_exits.len() > 1 && !is_duplicate_recorded(&failures, exit) {
|
||||
failures.push(CoherenceFailure::DuplicateExits(duplicate_exits));
|
||||
}
|
||||
}
|
||||
|
||||
failures
|
||||
}
|
||||
|
||||
/// Attempt to reconnect back to the connected scene. The model is not
|
||||
/// always good at this. Here, we correct it by attempting to find the
|
||||
/// exit and making sure the direction is coherently reversed. A
|
||||
/// linkback exit is created from scratch if one cannot be found.
|
||||
pub fn make_scene_from_stub_coherent(
|
||||
&self,
|
||||
content: &mut ContentContainer,
|
||||
connected_scene: &Scene,
|
||||
) {
|
||||
let new_scene = content.owner.as_scene_mut();
|
||||
let connected_key = connected_scene._key.as_deref().unwrap();
|
||||
let connected_id = connected_scene._id.as_deref().unwrap();
|
||||
|
||||
let direction_from = connected_scene
|
||||
.exits
|
||||
.iter()
|
||||
.find(|exit| &exit.scene_key == new_scene._key.as_ref().unwrap())
|
||||
.map(|exit| exit.direction.as_ref())
|
||||
.unwrap_or("from");
|
||||
|
||||
let reversed_direction = reverse_direction(direction_from);
|
||||
|
||||
// 1. Delete any exit that is from the reversed direction, or
|
||||
// has the name/ID/key of the connected scene.
|
||||
let mut stubs_to_delete = vec![];
|
||||
let keep_exit = |exit: &Exit| {
|
||||
!(exit.direction == reversed_direction
|
||||
|| Some(exit.scene_key.as_ref()) == connected_scene._key.as_deref()
|
||||
|| exit.scene_id.as_deref() == connected_scene._id.as_deref()
|
||||
|| exit.name.to_lowercase() == connected_scene.name.to_lowercase()
|
||||
|| exit.name == connected_key
|
||||
|| exit.name == connected_id)
|
||||
};
|
||||
|
||||
new_scene.exits.retain_mut(|exit| {
|
||||
let keep = keep_exit(exit);
|
||||
if !keep {
|
||||
stubs_to_delete.push(mem::take(&mut exit.scene_key));
|
||||
}
|
||||
keep
|
||||
});
|
||||
|
||||
// 2. Delete corresponding scene stubs
|
||||
content.contained.retain(|c| match &c.content {
|
||||
Content::SceneStub(stub) => match stub._key.as_ref() {
|
||||
Some(key) => !stubs_to_delete.contains(key),
|
||||
_ => true,
|
||||
},
|
||||
_ => true,
|
||||
});
|
||||
|
||||
// 3. Add new linkback exit
|
||||
let exit = Exit::from_connected_scene(connected_scene, &reversed_direction);
|
||||
new_scene.exits.push(exit);
|
||||
}
|
||||
|
||||
pub async fn make_scene_coherent(&self, content: &mut ContentContainer) -> Result<()> {
|
||||
let scene = content.owner.as_scene_mut();
|
||||
let failures = self.check_scene_coherence(&scene);
|
||||
let fixes = self.generator.fix_scene(&scene, failures).await?;
|
||||
let mut deletes = vec![]; // Needed for call to Vec::retain after the fact
|
||||
|
||||
for fix in fixes {
|
||||
match fix {
|
||||
SceneFix::FixedExit {
|
||||
index,
|
||||
new: fixed_exit,
|
||||
} => {
|
||||
let old_exit_key = scene.exits[index].scene_key.as_str();
|
||||
|
||||
content.contained.retain(|c| match &c.content {
|
||||
Content::SceneStub(stub) => stub._key.as_deref() != Some(old_exit_key),
|
||||
_ => true,
|
||||
});
|
||||
|
||||
scene.exits[index] = fixed_exit.into();
|
||||
let fixed_exit = &scene.exits[index];
|
||||
|
||||
content
|
||||
.contained
|
||||
.push(ContentRelation::scene_stub(SceneStub::from(fixed_exit)));
|
||||
}
|
||||
SceneFix::DeleteExit(index) => {
|
||||
deletes.push(index);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Deletes
|
||||
let mut index: usize = 0;
|
||||
scene.exits.retain(|_| {
|
||||
let keep_it = !deletes.contains(&index);
|
||||
index += 1;
|
||||
keep_it
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -163,7 +163,6 @@ impl AiConversation {
|
|||
|
||||
prompt_so_far.push_str(&prompt.prompt);
|
||||
|
||||
// Will fail ?
|
||||
let input = create_input(
|
||||
self.gen_key.clone(),
|
||||
&prompt_so_far,
|
||||
|
|
|
@ -20,20 +20,27 @@ pub mod prompts;
|
|||
use convo::AiPrompt;
|
||||
use generator::AiClient;
|
||||
|
||||
use self::coherence::AiCoherence;
|
||||
|
||||
/// Highest-level AI/LLM construct, which returns fully converted game
|
||||
/// objects to us. Basically, call the mid-level `client` to create
|
||||
/// seed objects, then call the mid level client again to detail the
|
||||
/// entities from their seeds. Then, stick a DB ID on them and put
|
||||
/// them in the database(?).
|
||||
pub struct AiLogic {
|
||||
generator: AiClient,
|
||||
generator: Rc<AiClient>,
|
||||
coherence: AiCoherence,
|
||||
db: Rc<Database>,
|
||||
}
|
||||
|
||||
impl AiLogic {
|
||||
pub fn new(api_client: Rc<KoboldClient>, db: &Rc<Database>) -> AiLogic {
|
||||
let generator = Rc::new(AiClient::new(api_client));
|
||||
let coherence = AiCoherence::new(generator.clone());
|
||||
|
||||
AiLogic {
|
||||
generator: AiClient::new(api_client),
|
||||
generator,
|
||||
coherence,
|
||||
db: db.clone(),
|
||||
}
|
||||
}
|
||||
|
@ -170,8 +177,9 @@ impl AiLogic {
|
|||
// directions and stuff, while the second is the normal scene
|
||||
// coherence (that can invoke the LLM).
|
||||
let mut content = self.fill_in_scene_from_stub(seed, stub).await?;
|
||||
coherence::make_scene_from_stub_coherent(&mut content, connected_scene);
|
||||
coherence::make_scene_coherent(&mut self.generator, &mut content).await?;
|
||||
self.coherence
|
||||
.make_scene_from_stub_coherent(&mut content, connected_scene);
|
||||
self.coherence.make_scene_coherent(&mut content).await?;
|
||||
|
||||
self.generator.reset_world_creation();
|
||||
|
||||
|
@ -191,7 +199,7 @@ impl AiLogic {
|
|||
.await?;
|
||||
|
||||
let mut content = self.fill_in_scene(scene_seed).await?;
|
||||
coherence::make_scene_coherent(&mut self.generator, &mut content).await?;
|
||||
self.coherence.make_scene_coherent(&mut content).await?;
|
||||
|
||||
self.generator.reset_world_creation();
|
||||
Ok(content)
|
||||
|
@ -237,9 +245,7 @@ impl AiLogic {
|
|||
|
||||
let mut stubs: Vec<_> = exits
|
||||
.iter()
|
||||
.map(|exit| {
|
||||
ContentRelation::scene_stub(SceneStub::from(exit))
|
||||
})
|
||||
.map(|exit| ContentRelation::scene_stub(SceneStub::from(exit)))
|
||||
.collect();
|
||||
|
||||
let mut scene = Scene {
|
||||
|
|
Loading…
Reference in New Issue