kibi-core 0.19.0 → 0.21.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/package.json +1 -1
- package/src/discovery.pl +449 -1
- package/src/kb.pl +38 -0
package/package.json
CHANGED
package/src/discovery.pl
CHANGED
|
@@ -12,7 +12,11 @@
|
|
|
12
12
|
requirement_proof_json/7,
|
|
13
13
|
symbol_proof_json/7,
|
|
14
14
|
graph_expand_json/8,
|
|
15
|
-
search_answer_verdicts_json/2
|
|
15
|
+
search_answer_verdicts_json/2,
|
|
16
|
+
ask_predicate_json/9,
|
|
17
|
+
ask_property_json/6,
|
|
18
|
+
ask_schemas_json/5,
|
|
19
|
+
ask_fact_kind_json/4
|
|
16
20
|
]).
|
|
17
21
|
|
|
18
22
|
% Source contains non-ASCII text; do not depend on the host locale.
|
|
@@ -471,6 +475,450 @@ graph_expand_json(SeedIds, Relationships, Direction, Depth, EntityTypes, MaxNode
|
|
|
471
475
|
Response = _{nodes: Nodes, edges: Edges, truncated: Truncated, meta: Meta},
|
|
472
476
|
dict_json_string(Response, JsonString).
|
|
473
477
|
|
|
478
|
+
%% ask_predicate_json(+Namespace, +Name, +ArgSpec, +PolarityFilter, +Scope, +DistinctArg, +Limit, +Offset, -JsonString)
|
|
479
|
+
% Typed query over ontology predicate facts (fact_kind: predicate). Namespace
|
|
480
|
+
% `none` matches any namespace. ArgSpec is either a positional list of
|
|
481
|
+
% constants where the atom '$any' is a wildcard ([] matches any arguments),
|
|
482
|
+
% or where(Pairs) with Name=Value entries resolved through the predicate's
|
|
483
|
+
% declared argument names (schema or built-in signature). PolarityFilter is
|
|
484
|
+
% `none`, assert or deny; Scope `current_requirements` keeps only facts that a
|
|
485
|
+
% current requirement requires; DistinctArg names a declared argument whose
|
|
486
|
+
% distinct values are counted. Rows carry the effective allow/deny decision
|
|
487
|
+
% when the predicate is a decision predicate. An empty answer lists, per
|
|
488
|
+
% bound argument, the values that do exist (suggestions). Read-only.
|
|
489
|
+
% implements REQ-kibi-query-fact-filters
|
|
490
|
+
ask_predicate_json(Namespace, Name, ArgSpec, PolarityFilter, Scope, DistinctArg, Limit, Offset, JsonString) :-
|
|
491
|
+
ask_namespace(Namespace, NamespaceMatch),
|
|
492
|
+
% The schema lookup may bind a namespace; an unnamed namespace must keep
|
|
493
|
+
% matching facts in every namespace, so the schema sees a copy.
|
|
494
|
+
copy_term(NamespaceMatch, SchemaNamespace),
|
|
495
|
+
ask_schema_dict(SchemaNamespace, Name, SchemaDict, ArgumentNames),
|
|
496
|
+
ask_arg_pattern(ArgSpec, ArgumentNames, ArgPattern, PatternWarnings),
|
|
497
|
+
( PatternWarnings == []
|
|
498
|
+
-> findall(Row,
|
|
499
|
+
ask_predicate_row(NamespaceMatch, Name, ArgPattern, PolarityFilter, Scope, ArgumentNames, Row),
|
|
500
|
+
Rows0)
|
|
501
|
+
; Rows0 = []
|
|
502
|
+
),
|
|
503
|
+
sort_dict_rows(Rows0, SortedRows),
|
|
504
|
+
length(SortedRows, Count),
|
|
505
|
+
paginate_rows(SortedRows, Offset, Limit, Rows),
|
|
506
|
+
ask_distinct(DistinctArg, ArgumentNames, SortedRows, Distinct, DistinctWarnings),
|
|
507
|
+
append(PatternWarnings, DistinctWarnings, Warnings),
|
|
508
|
+
ask_suggestions(Count, NamespaceMatch, Name, ArgSpec, ArgumentNames, Suggestions),
|
|
509
|
+
ask_spec_json(ArgSpec, ArgPattern, SpecJson),
|
|
510
|
+
ask_optional_atom(Namespace, NamespaceJson),
|
|
511
|
+
status_meta_dict(Meta),
|
|
512
|
+
Response = _{
|
|
513
|
+
mode: predicate,
|
|
514
|
+
predicate: _{namespace: NamespaceJson, name: Name, args: SpecJson},
|
|
515
|
+
schema: SchemaDict,
|
|
516
|
+
count: Count,
|
|
517
|
+
rows: Rows,
|
|
518
|
+
distinct: Distinct,
|
|
519
|
+
suggestions: Suggestions,
|
|
520
|
+
warnings: Warnings,
|
|
521
|
+
meta: Meta
|
|
522
|
+
},
|
|
523
|
+
dict_json_string(Response, JsonString).
|
|
524
|
+
|
|
525
|
+
%% ask_arg_pattern(+ArgSpec, +ArgumentNames, -ArgPattern, -Warnings)
|
|
526
|
+
% A positional spec is used as is. A where(Pairs) spec becomes a positional
|
|
527
|
+
% pattern over the declared argument names; an undeclared name yields a
|
|
528
|
+
% warning that lists the declared names and no rows.
|
|
529
|
+
ask_arg_pattern(where(Pairs), ArgumentNames, ArgPattern, Warnings) :-
|
|
530
|
+
!,
|
|
531
|
+
( is_list(ArgumentNames)
|
|
532
|
+
-> findall(Warning,
|
|
533
|
+
( member(Key=_, Pairs),
|
|
534
|
+
\+ memberchk(Key, ArgumentNames),
|
|
535
|
+
atomic_list_concat(ArgumentNames, ', ', Declared),
|
|
536
|
+
format(atom(Warning), "'~w' is not an argument of this predicate; declared arguments: ~w", [Key, Declared])
|
|
537
|
+
),
|
|
538
|
+
Warnings),
|
|
539
|
+
findall(Entry,
|
|
540
|
+
( member(ArgName, ArgumentNames),
|
|
541
|
+
( memberchk(ArgName=Value, Pairs)
|
|
542
|
+
-> Entry = Value
|
|
543
|
+
; Entry = '$any'
|
|
544
|
+
)
|
|
545
|
+
),
|
|
546
|
+
ArgPattern)
|
|
547
|
+
; Pairs == []
|
|
548
|
+
-> ArgPattern = [],
|
|
549
|
+
Warnings = []
|
|
550
|
+
; ArgPattern = [],
|
|
551
|
+
Warnings = ["named arguments need a predicate schema; none declares this predicate, so pass positional args instead"]
|
|
552
|
+
).
|
|
553
|
+
ask_arg_pattern(ArgPattern, ArgumentNames, ArgPattern, Warnings) :-
|
|
554
|
+
( is_list(ArgumentNames),
|
|
555
|
+
ArgPattern \== [],
|
|
556
|
+
length(ArgPattern, Given),
|
|
557
|
+
length(ArgumentNames, Arity),
|
|
558
|
+
Given =\= Arity
|
|
559
|
+
-> format(atom(Warning), "positional args need one entry per argument (~w declared, ~w given); use null for any value or name them with where", [Arity, Given]),
|
|
560
|
+
Warnings = [Warning]
|
|
561
|
+
; Warnings = []
|
|
562
|
+
).
|
|
563
|
+
|
|
564
|
+
ask_spec_json(where(Pairs), _ArgPattern, Json) :-
|
|
565
|
+
!,
|
|
566
|
+
findall(Key-Value, member(Key=Value, Pairs), KeyValues),
|
|
567
|
+
dict_pairs(Json, _, KeyValues).
|
|
568
|
+
ask_spec_json(_ArgSpec, ArgPattern, Json) :-
|
|
569
|
+
ask_pattern_json(ArgPattern, Json).
|
|
570
|
+
|
|
571
|
+
%% ask_suggestions(+Count, +Namespace, +Name, +ArgSpec, +ArgumentNames, -Suggestions)
|
|
572
|
+
% When nothing matched a where(...) spec, list for each bound argument the
|
|
573
|
+
% values that do exist for this predicate so the caller can correct the
|
|
574
|
+
% question instead of reading "no results".
|
|
575
|
+
ask_suggestions(0, Namespace, Name, where(Pairs), ArgumentNames, Suggestions) :-
|
|
576
|
+
is_list(ArgumentNames),
|
|
577
|
+
Pairs \== [],
|
|
578
|
+
findall(Key-Values,
|
|
579
|
+
( member(Key=_, Pairs),
|
|
580
|
+
nth1(Position, ArgumentNames, Key),
|
|
581
|
+
findall(Value,
|
|
582
|
+
( kb:predicate_fact(_, Namespace, Name, Args, _),
|
|
583
|
+
nth1(Position, Args, Value)
|
|
584
|
+
),
|
|
585
|
+
Values0),
|
|
586
|
+
sort(Values0, Values)
|
|
587
|
+
),
|
|
588
|
+
Entries),
|
|
589
|
+
Entries \== [],
|
|
590
|
+
!,
|
|
591
|
+
dict_pairs(Suggestions, _, Entries).
|
|
592
|
+
ask_suggestions(_Count, _Namespace, _Name, _ArgSpec, _ArgumentNames, null).
|
|
593
|
+
|
|
594
|
+
ask_namespace(none, _) :- !.
|
|
595
|
+
ask_namespace(Namespace, Namespace).
|
|
596
|
+
|
|
597
|
+
ask_optional_atom(none, null) :- !.
|
|
598
|
+
ask_optional_atom(Atom, Atom).
|
|
599
|
+
|
|
600
|
+
ask_pattern_json([], []) :- !.
|
|
601
|
+
ask_pattern_json(Pattern, Json) :-
|
|
602
|
+
maplist(ask_pattern_entry_json, Pattern, Json).
|
|
603
|
+
|
|
604
|
+
ask_pattern_entry_json('$any', null) :- !.
|
|
605
|
+
ask_pattern_entry_json(Value, Value).
|
|
606
|
+
|
|
607
|
+
% Arguments of a stored fact unify with the requested pattern; '$any' entries
|
|
608
|
+
% become fresh variables and an empty pattern accepts any arity.
|
|
609
|
+
ask_pattern_args([], _Args) :- !.
|
|
610
|
+
ask_pattern_args(Pattern, Args) :-
|
|
611
|
+
maplist(ask_pattern_arg, Pattern, Args).
|
|
612
|
+
|
|
613
|
+
ask_pattern_arg('$any', _) :- !.
|
|
614
|
+
ask_pattern_arg(Value, Arg) :-
|
|
615
|
+
kb:normalize_term_atom(Value, Arg).
|
|
616
|
+
|
|
617
|
+
ask_polarity_match(none, _) :- !.
|
|
618
|
+
ask_polarity_match(Polarity, Polarity).
|
|
619
|
+
|
|
620
|
+
% implements REQ-kibi-query-fact-filters
|
|
621
|
+
ask_predicate_row(Namespace, Name, ArgPattern, PolarityFilter, Scope, ArgumentNames, Row) :-
|
|
622
|
+
kb:predicate_fact(FactId, Namespace, Name, Args, Polarity),
|
|
623
|
+
ask_pattern_args(ArgPattern, Args),
|
|
624
|
+
ask_polarity_match(PolarityFilter, Polarity),
|
|
625
|
+
ask_fact_requirements(FactId, requires_predicate, Requirements, HasCurrent),
|
|
626
|
+
ask_scope_match(Scope, HasCurrent),
|
|
627
|
+
ask_fact_text(FactId, Title, Status, ClaimText, CanonicalKey),
|
|
628
|
+
ask_named_arguments(ArgumentNames, Args, NamedArguments),
|
|
629
|
+
ask_effect(Namespace, Name, Args, Polarity, Effect),
|
|
630
|
+
Row = _{
|
|
631
|
+
id: FactId,
|
|
632
|
+
type: fact,
|
|
633
|
+
title: Title,
|
|
634
|
+
status: Status,
|
|
635
|
+
namespace: Namespace,
|
|
636
|
+
name: Name,
|
|
637
|
+
args: Args,
|
|
638
|
+
arguments: NamedArguments,
|
|
639
|
+
polarity: Polarity,
|
|
640
|
+
effect: Effect,
|
|
641
|
+
canonicalKey: CanonicalKey,
|
|
642
|
+
claimText: ClaimText,
|
|
643
|
+
requirements: Requirements
|
|
644
|
+
}.
|
|
645
|
+
|
|
646
|
+
ask_scope_match(current_requirements, true) :- !.
|
|
647
|
+
ask_scope_match(current_requirements, _) :- !, fail.
|
|
648
|
+
ask_scope_match(_Scope, _HasCurrent).
|
|
649
|
+
|
|
650
|
+
%% ask_fact_requirements(+FactId, +Relationship, -Requirements, -HasCurrent)
|
|
651
|
+
% Requirements linked to the fact through Relationship, each with whether it
|
|
652
|
+
% is current (kb:current_req/1); HasCurrent is true when at least one is.
|
|
653
|
+
ask_fact_requirements(FactId, Relationship, Requirements, HasCurrent) :-
|
|
654
|
+
findall(ReqId, kb_relationship(Relationship, ReqId, FactId), ReqIds0),
|
|
655
|
+
sort(ReqIds0, ReqIds),
|
|
656
|
+
maplist(ask_requirement_dict, ReqIds, Requirements),
|
|
657
|
+
( member(Dict, Requirements), Dict.current == true
|
|
658
|
+
-> HasCurrent = true
|
|
659
|
+
; HasCurrent = false
|
|
660
|
+
).
|
|
661
|
+
|
|
662
|
+
ask_requirement_dict(ReqId, _{id: ReqId, status: Status, current: Current}) :-
|
|
663
|
+
( kb_entity(ReqId, req, Props), memberchk(status=StatusRaw, Props)
|
|
664
|
+
-> source_value_atom(StatusRaw, Status)
|
|
665
|
+
; Status = ''
|
|
666
|
+
),
|
|
667
|
+
( kb:current_req(ReqId) -> Current = true ; Current = false ).
|
|
668
|
+
|
|
669
|
+
ask_fact_text(FactId, Title, Status, ClaimText, CanonicalKey) :-
|
|
670
|
+
kb_entity(FactId, fact, Props),
|
|
671
|
+
( memberchk(title=TitleRaw, Props) -> source_value_atom(TitleRaw, Title) ; Title = '' ),
|
|
672
|
+
( memberchk(status=StatusRaw, Props) -> source_value_atom(StatusRaw, Status) ; Status = '' ),
|
|
673
|
+
( memberchk(claim_text=ClaimRaw, Props) -> source_value_atom(ClaimRaw, ClaimText) ; ClaimText = '' ),
|
|
674
|
+
( memberchk(canonical_key=KeyRaw, Props) -> source_value_atom(KeyRaw, CanonicalKey) ; CanonicalKey = '' ).
|
|
675
|
+
|
|
676
|
+
% Named arguments follow the schema when it exists and has the same arity;
|
|
677
|
+
% otherwise the row carries only the positional list.
|
|
678
|
+
ask_named_arguments(ArgumentNames, Args, Named) :-
|
|
679
|
+
is_list(ArgumentNames),
|
|
680
|
+
same_length(ArgumentNames, Args),
|
|
681
|
+
!,
|
|
682
|
+
pairs_keys_values(Pairs, ArgumentNames, Args),
|
|
683
|
+
dict_pairs(Named, _, Pairs).
|
|
684
|
+
ask_named_arguments(_ArgumentNames, _Args, null).
|
|
685
|
+
|
|
686
|
+
% The effective allow/deny decision of a decision predicate (a schema with
|
|
687
|
+
% key_arguments and a decision argument, or the built-in permission_rule).
|
|
688
|
+
ask_effect(Namespace, Name, Args, Polarity, Effect) :-
|
|
689
|
+
length(Args, Arity),
|
|
690
|
+
kb:decision_predicate(Namespace, Name, Arity, _KeyPositions, DecisionPosition),
|
|
691
|
+
nth1(DecisionPosition, Args, Decision),
|
|
692
|
+
kb:effective_decision(Decision, Polarity, Effect0),
|
|
693
|
+
!,
|
|
694
|
+
Effect = Effect0.
|
|
695
|
+
ask_effect(_Namespace, _Name, _Args, _Polarity, null).
|
|
696
|
+
|
|
697
|
+
%% ask_schema_dict(?Namespace, +Name, -SchemaDict, -ArgumentNames)
|
|
698
|
+
% The project predicate_schema for the predicate, the built-in decision
|
|
699
|
+
% signature, or null when neither exists.
|
|
700
|
+
ask_schema_dict(Namespace, Name, SchemaDict, ArgumentNames) :-
|
|
701
|
+
kb:predicate_schema(SchemaId, Namespace, Name, Arity, ArgumentNames, ArgumentTypes),
|
|
702
|
+
!,
|
|
703
|
+
ask_schema_row(SchemaId, Namespace, Name, Arity, ArgumentNames, ArgumentTypes, SchemaDict).
|
|
704
|
+
ask_schema_dict(Namespace, Name, SchemaDict, ArgumentNames) :-
|
|
705
|
+
kb:builtin_decision_predicate(Namespace, Name, Arity, KeyPositions, DecisionPosition),
|
|
706
|
+
!,
|
|
707
|
+
ask_builtin_schema_row(Namespace, Name, Arity, KeyPositions, DecisionPosition, SchemaDict),
|
|
708
|
+
ArgumentNames = SchemaDict.argumentNames.
|
|
709
|
+
ask_schema_dict(_Namespace, _Name, null, none).
|
|
710
|
+
|
|
711
|
+
ask_builtin_schema_row(Namespace, Name, Arity, KeyPositions, DecisionPosition, Row) :-
|
|
712
|
+
builtin_argument_names(Name, Names),
|
|
713
|
+
findall(Key, (member(P, KeyPositions), nth1(P, Names, Key)), KeyArguments),
|
|
714
|
+
nth1(DecisionPosition, Names, DecisionArgument),
|
|
715
|
+
aggregate_all(count, kb:predicate_fact(_, Namespace, Name, _, _), FactCount),
|
|
716
|
+
Row = _{
|
|
717
|
+
id: null,
|
|
718
|
+
builtin: true,
|
|
719
|
+
namespace: Namespace,
|
|
720
|
+
name: Name,
|
|
721
|
+
arity: Arity,
|
|
722
|
+
argumentNames: Names,
|
|
723
|
+
argumentTypes: Names,
|
|
724
|
+
keyArguments: KeyArguments,
|
|
725
|
+
decisionArgument: DecisionArgument,
|
|
726
|
+
argumentConstants: null,
|
|
727
|
+
examples: [],
|
|
728
|
+
factCount: FactCount
|
|
729
|
+
}.
|
|
730
|
+
|
|
731
|
+
% Keep in step with builtin_decision_predicate/5 in kb.pl.
|
|
732
|
+
builtin_argument_names(permission_rule, [actor, action, resource, decision]).
|
|
733
|
+
|
|
734
|
+
ask_schema_row(SchemaId, Namespace, Name, Arity, ArgumentNames, ArgumentTypes, Row) :-
|
|
735
|
+
kb_entity(SchemaId, fact, Props),
|
|
736
|
+
( memberchk(key_arguments=KeysRaw, Props)
|
|
737
|
+
-> kb:normalize_term_atom_list(KeysRaw, KeyArguments)
|
|
738
|
+
; KeyArguments = []
|
|
739
|
+
),
|
|
740
|
+
( kb:decision_predicate(Namespace, Name, Arity, _, DecisionPosition),
|
|
741
|
+
nth1(DecisionPosition, ArgumentNames, DecisionArgument0)
|
|
742
|
+
-> DecisionArgument = DecisionArgument0
|
|
743
|
+
; DecisionArgument = null
|
|
744
|
+
),
|
|
745
|
+
( memberchk(argument_constants=ConstantsRaw, Props),
|
|
746
|
+
ask_json_value(ConstantsRaw, Constants0)
|
|
747
|
+
-> Constants = Constants0
|
|
748
|
+
; Constants = null
|
|
749
|
+
),
|
|
750
|
+
( memberchk(examples=ExamplesRaw, Props), is_list(ExamplesRaw)
|
|
751
|
+
-> maplist(source_value_atom, ExamplesRaw, Examples)
|
|
752
|
+
; Examples = []
|
|
753
|
+
),
|
|
754
|
+
aggregate_all(count, kb:predicate_fact(_, Namespace, Name, _, _), FactCount),
|
|
755
|
+
Row = _{
|
|
756
|
+
id: SchemaId,
|
|
757
|
+
builtin: false,
|
|
758
|
+
namespace: Namespace,
|
|
759
|
+
name: Name,
|
|
760
|
+
arity: Arity,
|
|
761
|
+
argumentNames: ArgumentNames,
|
|
762
|
+
argumentTypes: ArgumentTypes,
|
|
763
|
+
keyArguments: KeyArguments,
|
|
764
|
+
decisionArgument: DecisionArgument,
|
|
765
|
+
argumentConstants: Constants,
|
|
766
|
+
examples: Examples,
|
|
767
|
+
factCount: FactCount
|
|
768
|
+
}.
|
|
769
|
+
|
|
770
|
+
ask_json_value(^^(Value, _Type), Parsed) :- !, ask_json_value(Value, Parsed).
|
|
771
|
+
ask_json_value(literal(Value), Parsed) :- !, ask_json_value(Value, Parsed).
|
|
772
|
+
ask_json_value(Dict, Dict) :- is_dict(Dict), !.
|
|
773
|
+
ask_json_value(Raw, Parsed) :-
|
|
774
|
+
(atom(Raw) ; string(Raw)),
|
|
775
|
+
catch(atom_json_dict(Raw, Parsed, [value_string_as(string)]), _, fail).
|
|
776
|
+
|
|
777
|
+
%% ask_distinct(+DistinctArg, +ArgumentNames, +Rows, -Distinct, -Warnings)
|
|
778
|
+
ask_distinct(none, _ArgumentNames, _Rows, null, []) :- !.
|
|
779
|
+
ask_distinct(DistinctArg, ArgumentNames, Rows, Distinct, []) :-
|
|
780
|
+
is_list(ArgumentNames),
|
|
781
|
+
nth1(Position, ArgumentNames, DistinctArg),
|
|
782
|
+
!,
|
|
783
|
+
findall(Value, (member(Row, Rows), nth1(Position, Row.args, Value)), Values0),
|
|
784
|
+
msort(Values0, Values),
|
|
785
|
+
clumped(Values, Clumps),
|
|
786
|
+
findall(_{value: Value, count: Count}, member(Value-Count, Clumps), Entries),
|
|
787
|
+
Distinct = _{argument: DistinctArg, values: Entries}.
|
|
788
|
+
ask_distinct(DistinctArg, _ArgumentNames, _Rows, null, [Warning]) :-
|
|
789
|
+
format(atom(Warning), "distinct argument '~w' is not a declared argument of this predicate; no schema names it", [DistinctArg]).
|
|
790
|
+
|
|
791
|
+
%% ask_property_json(+SubjectKey, +PropertyKey, +Scope, +Limit, +Offset, -JsonString)
|
|
792
|
+
% Typed query over strict property_value facts of one subject key.
|
|
793
|
+
% PropertyKey `none` matches every property of the subject.
|
|
794
|
+
% implements REQ-kibi-query-fact-filters
|
|
795
|
+
ask_property_json(SubjectKey, PropertyKey, Scope, Limit, Offset, JsonString) :-
|
|
796
|
+
kb:normalize_term_atom(SubjectKey, Subject),
|
|
797
|
+
ask_namespace(PropertyKey, PropertyMatch),
|
|
798
|
+
findall(Row, ask_property_row(Subject, PropertyMatch, Scope, Row), Rows0),
|
|
799
|
+
sort_dict_rows(Rows0, SortedRows),
|
|
800
|
+
length(SortedRows, Count),
|
|
801
|
+
paginate_rows(SortedRows, Offset, Limit, Rows),
|
|
802
|
+
findall(SubjectId, kb:fact_subject_key(SubjectId, Subject), SubjectIds0),
|
|
803
|
+
sort(SubjectIds0, SubjectIds),
|
|
804
|
+
ask_optional_atom(PropertyKey, PropertyJson),
|
|
805
|
+
status_meta_dict(Meta),
|
|
806
|
+
Response = _{
|
|
807
|
+
mode: property,
|
|
808
|
+
property: _{subjectKey: Subject, propertyKey: PropertyJson},
|
|
809
|
+
subjectFacts: SubjectIds,
|
|
810
|
+
count: Count,
|
|
811
|
+
rows: Rows,
|
|
812
|
+
warnings: [],
|
|
813
|
+
meta: Meta
|
|
814
|
+
},
|
|
815
|
+
dict_json_string(Response, JsonString).
|
|
816
|
+
|
|
817
|
+
ask_property_row(Subject, PropertyMatch, Scope, Row) :-
|
|
818
|
+
kb:fact_property_tuple(FactId, Subject, Property, Op, ValType, Value, Unit, PropScope, Polarity),
|
|
819
|
+
( PropertyMatch == Property -> true ; var(PropertyMatch) ),
|
|
820
|
+
ask_fact_requirements(FactId, requires_property, Requirements, HasCurrent),
|
|
821
|
+
ask_scope_match(Scope, HasCurrent),
|
|
822
|
+
ask_fact_text(FactId, Title, Status, ClaimText, CanonicalKey),
|
|
823
|
+
ask_property_value_json(Value, ValueJson),
|
|
824
|
+
Row = _{
|
|
825
|
+
id: FactId,
|
|
826
|
+
type: fact,
|
|
827
|
+
title: Title,
|
|
828
|
+
status: Status,
|
|
829
|
+
subjectKey: Subject,
|
|
830
|
+
propertyKey: Property,
|
|
831
|
+
operator: Op,
|
|
832
|
+
valueType: ValType,
|
|
833
|
+
value: ValueJson,
|
|
834
|
+
unit: Unit,
|
|
835
|
+
scope: PropScope,
|
|
836
|
+
polarity: Polarity,
|
|
837
|
+
canonicalKey: CanonicalKey,
|
|
838
|
+
claimText: ClaimText,
|
|
839
|
+
requirements: Requirements
|
|
840
|
+
}.
|
|
841
|
+
|
|
842
|
+
ask_property_value_json(Value, Value) :- number(Value), !.
|
|
843
|
+
ask_property_value_json(Value, Json) :- source_value_atom(Value, Json).
|
|
844
|
+
|
|
845
|
+
%% ask_schemas_json(+Namespace, +Name, +Limit, +Offset, -JsonString)
|
|
846
|
+
% Catalog of the predicates an agent can ask about: every project
|
|
847
|
+
% predicate_schema fact plus built-in decision predicates without a project
|
|
848
|
+
% schema, with argument names, constants, key arguments and fact counts.
|
|
849
|
+
% implements REQ-kibi-query-fact-filters
|
|
850
|
+
ask_schemas_json(Namespace, Name, Limit, Offset, JsonString) :-
|
|
851
|
+
ask_namespace(Namespace, NamespaceMatch),
|
|
852
|
+
ask_namespace(Name, NameMatch),
|
|
853
|
+
findall(Row,
|
|
854
|
+
( kb:predicate_schema(SchemaId, NamespaceMatch, NameMatch, Arity, ArgumentNames, ArgumentTypes),
|
|
855
|
+
ask_schema_row(SchemaId, NamespaceMatch, NameMatch, Arity, ArgumentNames, ArgumentTypes, Row)
|
|
856
|
+
),
|
|
857
|
+
SchemaRows),
|
|
858
|
+
findall(Row,
|
|
859
|
+
( kb:builtin_decision_predicate(NamespaceMatch, NameMatch, Arity, KeyPositions, DecisionPosition),
|
|
860
|
+
\+ kb:predicate_schema(_, NamespaceMatch, NameMatch, Arity, _, _),
|
|
861
|
+
ask_builtin_schema_row(NamespaceMatch, NameMatch, Arity, KeyPositions, DecisionPosition, Row)
|
|
862
|
+
),
|
|
863
|
+
BuiltinRows),
|
|
864
|
+
append(SchemaRows, BuiltinRows, Rows0),
|
|
865
|
+
map_list_to_pairs(ask_schema_sort_key, Rows0, Pairs),
|
|
866
|
+
keysort(Pairs, SortedPairs),
|
|
867
|
+
pairs_values(SortedPairs, SortedRows),
|
|
868
|
+
length(SortedRows, Count),
|
|
869
|
+
paginate_rows(SortedRows, Offset, Limit, Rows),
|
|
870
|
+
status_meta_dict(Meta),
|
|
871
|
+
Response = _{mode: schemas, count: Count, rows: Rows, warnings: [], meta: Meta},
|
|
872
|
+
dict_json_string(Response, JsonString).
|
|
873
|
+
|
|
874
|
+
ask_schema_sort_key(Row, Key) :-
|
|
875
|
+
format(atom(Key), '~w:~w/~w', [Row.namespace, Row.name, Row.arity]).
|
|
876
|
+
|
|
877
|
+
%% ask_fact_kind_json(+Kind, +Limit, +Offset, -JsonString)
|
|
878
|
+
% Facts of one fact_kind (subject, property_value, predicate, observation,
|
|
879
|
+
% meta, rule, rule_schema) with their identifying typed fields only, so a
|
|
880
|
+
% caller can see what is modeled without paging full entities.
|
|
881
|
+
% implements REQ-kibi-query-fact-filters
|
|
882
|
+
ask_fact_kind_json(Kind, Limit, Offset, JsonString) :-
|
|
883
|
+
findall(Row, ask_fact_kind_row(Kind, Row), Rows0),
|
|
884
|
+
sort_dict_rows(Rows0, SortedRows),
|
|
885
|
+
length(SortedRows, Count),
|
|
886
|
+
paginate_rows(SortedRows, Offset, Limit, Rows),
|
|
887
|
+
status_meta_dict(Meta),
|
|
888
|
+
Response = _{mode: fact_kind, factKind: Kind, count: Count, rows: Rows, warnings: [], meta: Meta},
|
|
889
|
+
dict_json_string(Response, JsonString).
|
|
890
|
+
|
|
891
|
+
ask_fact_kind_row(Kind, Row) :-
|
|
892
|
+
kb_entity(FactId, fact, Props),
|
|
893
|
+
memberchk(fact_kind=KindRaw, Props),
|
|
894
|
+
kb:normalize_term_atom(KindRaw, Kind),
|
|
895
|
+
ask_fact_text(FactId, Title, Status, ClaimText, CanonicalKey),
|
|
896
|
+
findall(Key-Value,
|
|
897
|
+
( member(Key, [subject_key, property_key, operator, value_type, unit, predicate_namespace, predicate_name, predicate_args, polarity]),
|
|
898
|
+
memberchk(Key=Raw, Props),
|
|
899
|
+
ask_typed_value(Raw, Value)
|
|
900
|
+
),
|
|
901
|
+
Typed),
|
|
902
|
+
( Kind == property_value
|
|
903
|
+
-> ( memberchk(value_type=ValTypeRaw, Props)
|
|
904
|
+
-> kb:normalize_term_atom(ValTypeRaw, ValType)
|
|
905
|
+
; ValType = string
|
|
906
|
+
),
|
|
907
|
+
kb:value_from_props(Props, ValType, Value0),
|
|
908
|
+
ask_property_value_json(Value0, PropertyValue),
|
|
909
|
+
ValuePairs = [value-PropertyValue]
|
|
910
|
+
; ValuePairs = []
|
|
911
|
+
),
|
|
912
|
+
append(Typed, ValuePairs, Fields),
|
|
913
|
+
dict_pairs(Row, _, [id-FactId, type-fact, title-Title, status-Status, factKind-Kind, canonicalKey-CanonicalKey, claimText-ClaimText | Fields]).
|
|
914
|
+
|
|
915
|
+
ask_typed_value(Raw, Value) :-
|
|
916
|
+
is_list(Raw),
|
|
917
|
+
!,
|
|
918
|
+
kb:normalize_term_atom_list(Raw, Value).
|
|
919
|
+
ask_typed_value(Raw, Value) :-
|
|
920
|
+
source_value_atom(Raw, Value).
|
|
921
|
+
|
|
474
922
|
matching_gap_row(TypeFilter, MissingRelationships, PresentRelationships, Tags, SourceFilter, Row) :-
|
|
475
923
|
matching_gap_candidate(TypeFilter, MissingRelationships, PresentRelationships, Tags, SourceFilter, Row, Stub),
|
|
476
924
|
gap_row_visible(Tags, Stub).
|
package/src/kb.pl
CHANGED
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
kb_log_relationship_upsert/4,
|
|
39
39
|
kb_relationship/3,
|
|
40
40
|
symbol_owns_requirement/2,
|
|
41
|
+
kb_domain_contradiction_pairs_examined/1,
|
|
41
42
|
scenario_verified_by_test/2,
|
|
42
43
|
requirement_test_fallback_allowed/1,
|
|
43
44
|
test_satisfies_requirement_semantics/2,
|
|
@@ -2717,6 +2718,43 @@ req_conflict_witness(ReqA, ReqB, Witness) :-
|
|
|
2717
2718
|
right: Right
|
|
2718
2719
|
}.
|
|
2719
2720
|
|
|
2721
|
+
%% kb_domain_contradiction_pairs_examined(-Count)
|
|
2722
|
+
% implements REQ-search-answer-hygiene
|
|
2723
|
+
% How many distinct pairs of requirement facts the domain-contradictions
|
|
2724
|
+
% rule compared on the current KB: strict property facts of two current
|
|
2725
|
+
% requirements constraining the same subject and property, predicate facts
|
|
2726
|
+
% of two current requirements with the same namespace, name and ordered
|
|
2727
|
+
% arguments, and decision-predicate facts agreeing on every key argument.
|
|
2728
|
+
% A check that found no contradiction over zero pairs evaluated nothing, and
|
|
2729
|
+
% kb_check says so; the count never implies the pairs are consistent.
|
|
2730
|
+
kb_domain_contradiction_pairs_examined(Count) :-
|
|
2731
|
+
findall(FactA-FactB, domain_contradiction_candidate_pair(FactA, FactB), Pairs0),
|
|
2732
|
+
sort(Pairs0, Pairs),
|
|
2733
|
+
length(Pairs, Count).
|
|
2734
|
+
|
|
2735
|
+
domain_contradiction_candidate_pair(FactA, FactB) :-
|
|
2736
|
+
current_req(ReqA),
|
|
2737
|
+
current_req(ReqB),
|
|
2738
|
+
ReqA @< ReqB,
|
|
2739
|
+
kb_relationship(constrains, ReqA, SubjectFactA),
|
|
2740
|
+
kb_relationship(constrains, ReqB, SubjectFactB),
|
|
2741
|
+
fact_subject_key(SubjectFactA, SubjectKey),
|
|
2742
|
+
fact_subject_key(SubjectFactB, SubjectKey),
|
|
2743
|
+
effective_req_property_fact(ReqA, SubjectKey, FactA, PropertyKey, _, _, _, _, _, _, _, _),
|
|
2744
|
+
effective_req_property_fact(ReqB, SubjectKey, FactB, PropertyKey, _, _, _, _, _, _, _, _),
|
|
2745
|
+
FactA \= FactB.
|
|
2746
|
+
domain_contradiction_candidate_pair(FactA, FactB) :-
|
|
2747
|
+
current_req(ReqA),
|
|
2748
|
+
current_req(ReqB),
|
|
2749
|
+
ReqA @< ReqB,
|
|
2750
|
+
effective_req_predicate(ReqA, FactA, Namespace, Name, Args, _),
|
|
2751
|
+
effective_req_predicate(ReqB, FactB, Namespace, Name, Args, _),
|
|
2752
|
+
FactA \= FactB.
|
|
2753
|
+
domain_contradiction_candidate_pair(FactA, FactB) :-
|
|
2754
|
+
decision_signatures(Signatures),
|
|
2755
|
+
Signatures \== [],
|
|
2756
|
+
decision_conflict_pair(Signatures, _, _, FactA, FactB, _, _, _, _, _, _, _, _).
|
|
2757
|
+
|
|
2720
2758
|
%% decision_conflict_pair(+Signatures, ?ReqA, ?ReqB, -FactA, -FactB, -Namespace, -Name, -ArgsA, -ArgsB, -PolarityA, -PolarityB, -KeyPositions, -DecisionPosition)
|
|
2721
2759
|
% Two decision-predicate facts of current requirements ReqA @< ReqB that
|
|
2722
2760
|
% agree on every key argument. Starts from the bound requirement.
|